提问人:duncan 提问时间:9/27/2023 最后编辑:Wyckduncan 更新时间:9/27/2023 访问量:111
意外需要的 C++ 模板专项课程
Unexpectedly required C++ Template Specialization
问:
我正在尝试为对象容器实现一个通用的 setter。Foo
我不明白为什么 clang 抱怨这段代码,因为:
您可以完全将 or 文本传递给作为参数的函数
std::string
const std::string &
模板专用化(确实会编译并具有预期行为)具有与模板化版本完全相同的代码。
知道我做错了什么吗?
谢谢。
#include <iostream>
#include <string>
#include <vector>
class Foo
{
protected:
int _x;
std::string _name;
public:
void set_x(int x) { _x = x; }
void set_name(const std::string & name) { _name = name; }
int x() const { return _x; }
std::string name() const { return _name; }
};
template <typename T, typename Container>
void set_property(Container & container, const T & value, void (Foo::*setter)(T))
{
for (auto & element : container)
(element.*setter)(value);
}
// Uncomment this to succeed.
//
//template <typename Container>
//void set_property(Container & container, const std::string & value, void (Foo::*setter)(const //std::string &))
//{
// for (auto & element : container)
// (element.*setter)(value);
//}
int main(void)
{
int x = 42;
std::string name = "foo";
// Initialization.
std::vector<Foo> foos(10);
for (auto & foo : foos)
{
foo.set_x(x);
foo.set_name(name);
}
x = 17;
name = "bar";
// Set properties.
set_property(foos, x, &Foo::set_x);
set_property(foos, name, &Foo::set_name);
std::cout << "x: " << foos[0].x() << std::endl;
std::cout << "name: " << foos[0].name() << std::endl;
return 0;
}
咔嚓咔嚓错误:
❯❯ g++ --std=c++20 foo.cpp &&./a.out
foo.cpp:44:5: error: no matching function for call to 'set_property'
set_property(foos, name, &Foo::set_name);
^~~~~~~~~~~~
foo.cpp:20:6: note: candidate template ignored: deduced conflicting types for parameter 'T' ('std::string' (aka 'basic_string<char>') vs. 'const std::string &' (aka 'const basic_string<char> &'))
void set_property(Container & container, const T & value, void (Foo::*setter)(T))
^
1 error generated.
答:
0赞
n. m. could be an AI
9/27/2023
#1
如果多次推导模板参数,则该参数必须在整个集合中完全匹配。
你有:
template <typename T, typename Container>
void set_property(Container & container, const T & value, void (Foo::*setter)(T))
请注意如何在两个单独的函数参数中使用,以及 。T
value
setter
现在,当您致电时
set_property(foos, name, &Foo::set_name);
T
推导了两次,分别从 和 中推导出。两者应该完全相同。但他们不是,说它一定是,而说它一定是.name
set_name
T
name
std::string
set_name
const std::string&
你说“哦,但可以在哪里使用”。这是真的,但无关紧要。两者必须相同。几乎不一样,在某种程度上不兼容,但相同。std::string&
const std::string&
T
如果你取消注释硬编码的重载(这不是一个专业化),你就可以完全开始了,因为现在没有相互冲突的推论。std::string
评论
0赞
duncan
9/27/2023
明白了。。。(注意:你对“不是专业化”的看法是完全正确的)
0赞
duncan
9/27/2023
#2
所以,多亏了@n-m-could-be-an-ai,我想出了这个解决方案
多谢
干杯
@Eugene评论后更新了解决方案
class Foo
{
protected:
int _x;
std::string _name;
public:
void set_x(int x) { _x = x; }
void set_name(const std::string & name) { _name = name; }
int x() const { return _x; }
std::string name() const { return _name; }
};
template <typename Container, typename T, typename Setter>
void container_set(Container & container, const T & value, Setter setter, std::type_identity_t<const T> * = nullptr)
{
for (auto & foo : container)
{
(foo.*setter)(value);
}
}
评论
0赞
Eugene
9/27/2023
您可以将其最后一个参数放在非推导上下文中,而不是复制函数。如果您使用的是 C++20,则有一个标准:en.cppreference.com/w/cpp/types/type_identity。如果没有,自己做是微不足道的.type_identity
0赞
duncan
9/27/2023
@Eugene。正确。多谢。虽然我不得不承认这个解决方案是由 Copilot 生成的,并且包含相当多的在我看来像黑魔法的东西......
1赞
ildjarn
9/27/2023
副驾驶给你的解决方案是典型的机器人垃圾——它可能会“工作”,但这不是 Eugene 的建议,也不会通过代码审查
0赞
Eugene
9/28/2023
如果删除无意义的 ,那么剩下的就是另一种解决方案 - 为您的成员函数类型提供单独的模板参数。, std::type_identity_t<const T> * = nullptr
评论
T
std::string
T
const std::string&
string
const std::string &
int
const int &