tl; dr:如果前面带有复制构造函数,则不会调用它template<typename Something>,并且Something在构造函数签名中使用它.
概念验证(ideone):
#include <cstdio>
template<typename T>
class Test {
private:
T value_;
public:
mutable bool has_copies;
Test(const T& value) : value_(value), has_copies(false) {}
template<typename Related> // remove template here to fix behavior
Test(const Test<Related>& t) {
printf("In copy constructor\n");
value_ = t.value_;
has_copies = true;
t.has_copies = true;
}
};
int main() {
Test<int> t1(42);
printf("Before constructor\n");
Test<int> t2(t1);
printf("After constructor:\n");
printf(" t1 thinks it %s copies\n", t1.has_copies ? "has" : "doesn't have");
printf(" t2 thinks it %s copies\n", t2.has_copies ? "has" : "doesn't have");
}
Run Code Online (Sandbox Code Playgroud)
输出:
Before constructor
After constructor:
t1 thinks it doesn't have copies
t2 thinks it doesn't have copies
Run Code Online (Sandbox Code Playgroud)
请注意如何In copy constructor打印.
使复制构造函数非模板化(即删除template<typename Related>并更改Related为T签名)可以解决我的问题.
$ g++ --version
g++ (GCC) 6.2.1 20160830
Run Code Online (Sandbox Code Playgroud)
编译和执行命令:
g++ -Wall -Wextra -pedantic -std=c++11 -O2 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_FORTIFY_SOURCE=2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector "test.cpp" -o "test" && "test"
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?
函数模板永远不能是复制构造函数,即使实例化的类型显然使其与复制构造函数的预期签名匹配.见[class.copy/2]:
如果X类的第一个参数是X&,const X&,volatile X&或const volatile X&,并且没有其他参数或者所有其他参数都有默认参数,则类X的非模板构造函数是一个复制构造函数.
所有类都有一个隐式生成的复制构造函数,如果没有用户声明的复制构造函数(有时这可能被定义为已删除),则带有签名:
Test(Test const &) = default;
Run Code Online (Sandbox Code Playgroud)
在编写时Test<int> t2(t1);,重载决策选择复制构造函数而不是模板函数,因为非模板优先于模板,所有其他条件相同.
请注意,如果您将模板功能更改为,Test(Test<Related>& t)那么现在将通过复制构造函数选择它:"所有其他内容"不再相等,并且实例化模板以完全匹配Test<int>优先于转换添加const以匹配复制构造函数.