C++ 类层次结构中完全转发构造函数与复制构造函数的冲突
我最近在尝试用完美的转发构造函数实现类层次结构时遇到了一个问题。 考虑下面的例子:C++ 类层次结构中完全转发构造函数与复制构造函数的冲突,c++,c++11,copy-constructor,sfinae,perfect-forwarding,C++,C++11,Copy Constructor,Sfinae,Perfect Forwarding,我最近在尝试用完美的转发构造函数实现类层次结构时遇到了一个问题。 考虑下面的例子: struct TestBase { template<typename T> explicit TestBase(T&& t) : s(std::forward<T>(t)) {} // Compiler refers to this line in the error message TestBase(const TestBase& other) :
struct TestBase {
template<typename T>
explicit TestBase(T&& t) : s(std::forward<T>(t)) {} // Compiler refers to this line in the error message
TestBase(const TestBase& other) : s(other.s) {}
std::string s;
};
struct Test : public TestBase {
template<typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(other) {}
};
struct TestBase{
模板
显式TestBase(T&&T):s(std::forward(T)){}//编译器在错误消息中引用了这一行
TestBase(consttestbase&other):s(other.s){}
std::字符串s;
};
结构测试:公共测试库{
模板
显式测试(T&&T):测试库(std::forward(T)){}
测试(常量测试和其他):TestBase(其他){}
};
当我试图编译代码时,出现以下错误:
错误3错误C2664:'std::basic_string::basic_string(const std::basic_string&'):无法将参数1从'const Test'转换为'const std::basic_string&'
我的理解是,编译器将完美转发构造函数视为比复制构造函数更好的数学。例如,见。在没有类层次结构的其他实现中,我可以通过SFINAE禁用完美转发构造函数作为复制构造函数。例如,见。当我尝试将上述解决方案应用于此示例时,仍然无法使用相同的错误消息进行编译
我认为一个可能的解决方案是避免完美的转发,在构造函数中按值获取参数,然后从它们转移到类变量
所以我的问题是,是否有其他解决方案,或者在这种情况下,完美的转发是不可能的
更新:
原来我的问题很容易被误解。因此,我将尝试澄清一下我的意图和背景
- 代码是完整的,如问题中所示。没有创建其他对象或调用其他函数。试图编译发布的示例时出现错误
- 拥有完美的转发构造函数的目的是为了成员初始化,而不是为了拥有某种额外的副本构造函数。这里的原因是在使用临时对象初始化成员时保存一些对象副本(如Scott Meyers在会谈中提出的)
- 不幸的是,事实证明,完美的转发构造函数可能与其他重载构造函数(在本例中为复制构造函数)冲突
- 正如对这个问题的回答和评论所建议的那样:这里可能的解决方案是引入显式强制转换或使用单独的非模板构造函数(即,关于这个示例,有两个分别带有参数
和const string&
的构造函数)string&
std::basic\u string::basic\u string(const std::basic\u string&):
这意味着它适用于std::string
无法将参数1从'const Test'转换为'const std::basic_string&
实际上,无法从Test
转换为std::string
。但是,Test
有一个字符串成员,即std::string s代码>
结论:看起来你忘了在那个地方添加.s
。可能是在s(std::forward(t))
中
另一个可能的原因是,为了构造Test的实例,选择了构造函数的第一个重载,而不是第二个重载。请尝试将Test(const-Test&other):TestBase(static_-cast(other)){}
更改为Test(const-Test&other):TestBase(static_-cast(other)){}
第二个Test
构造函数正在调用TestBase,有两种可能。其中一个拿走任何东西,另一个拿走测试库。但是你通过了一个测试,“任何东西”都更匹配。通过显式地强制转换到TestBase常量&,我们应该能够得到正确的匹配项
另一种可能性可能涉及测试的构造方式——可能您传入的内容与要测试的模板构造函数相匹配?我们可以通过从测试中删除模板构造函数并查看错误是否消失来测试另一种可能性
如果是这种情况,为什么您链接的技术(当类型与测试匹配时禁用测试模板构造函数)不起作用?以下应该起作用,并且它不使用显式强制转换:
struct Test : public TestBase {
private:
static TestBase const& toBase(const Test& o) { return o; }
public:
template <typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(toBase(other)) {}
};
结构测试:公共测试库{
私人:
静态TestBase const&toBase(const Test&o){return o;}
公众:
模板
显式测试(T&&T):测试库(std::forward(T)){}
Test(const Test和other):TestBase(toBase(other)){}
};
可能是您编写了类似于std::string(test)
的内容,而不是std::string(test.s)
?请给我们看main.cc的第130行,您编写了s(std::forward(t))
而不是s(std::forward(t).s)
。通常,将重载和转发模板混合在一起是个坏主意。我想从string
创建一个构造函数,并添加一个转发make\u test
函数模板。@Andrey抱歉,我应该突出显示错误消息的来源。main.cc中的行号130表示TestBase中的完美转发构造函数。我将编辑我的示例…@avakar参数t不应该是测试类的对象,而是某种字符串(即std::string或cont char*)。我不希望完美的转发构造函数是副本构造函数,因此我认为不应该添加.s
我不认为我忘记了.s
。我的想法是,参数t本身是某种字符串,我想转发给成员。例如一个const char*或一个std::string。@mkh:那么这意味着不知何故T不是一个字符串,而是一个Test
。我的问题是如何使编译器停止认为T可能是一个测试。我不创建任何对象,也不调用任何函数。@Andrey yep--看来ctor 2在Test