C++ GCC:在需要复制构造函数时实例化模板构造函数

C++ GCC:在需要复制构造函数时实例化模板构造函数,c++,gcc,copy-constructor,C++,Gcc,Copy Constructor,在下面的示例中,GCC>=4.7实例化模板构造函数(您可以通过读取错误消息来观察),尽管只需要隐式生成的复制构造函数 #include <type_traits> // 'ambiguous' is ambiguous for 'ambiguous<int, int>' template<typename A, typename B> struct ambiguous : std::false_type {}; template<typename

在下面的示例中,
GCC>=4.7
实例化模板构造函数(您可以通过读取错误消息来观察),尽管只需要隐式生成的复制构造函数

#include <type_traits>

// 'ambiguous' is ambiguous for 'ambiguous<int, int>'
template<typename A, typename B> 
struct ambiguous : std::false_type {};

template<typename T> 
struct ambiguous<int, T> : std::true_type {};

template<typename T> 
struct ambiguous<T, int> : std::true_type {};

// quantity
template<typename Type>
class quantity
{
public:
    quantity() = default;

    // Copy-constructor is implicitly created

    // Template constructor
    template<
        typename T,
        typename = typename std::enable_if<ambiguous<Type, T>::value>::type
    >
    quantity(quantity<T>) {}

    template<
        typename T,
        typename = typename std::enable_if<ambiguous<Type, T>::value>::type
    >
    void set(quantity<T>) {}
};

// main
int main()
{   
    quantity<int> a;
    quantity<float> b;
    b.set(a);
}
因此,当调用
b.set(a)时
GCC
显然在寻找一个复制构造函数,并且在实例化模板构造函数的过程中,模板构造函数又实例化了
不明确的
,这是(uhm…)不明确的

问题:即使需要复制构造函数,
GCC
实例化模板构造函数是否正确?

GCC是正确的

这里有几个问题不幸地在你的问题中被混淆了:

首先,gcc<4.7的行为没有根本的不同;gcc的所有版本(至少)4.4拒绝了非常类似的程序:

struct S;

template<typename, typename> struct U {};
template<typename T> struct U<S, T> {};
template<typename T> struct U<T, S> {};

struct S {
  S() = default;
  template<typename T, typename = typename U<S, T>::type> S(T) {}
};

int main() {   
  S a;
  S b(a);
}
struct;
模板结构U{};
模板结构U{};
模板结构U{};
结构{
S()=默认值;
模板S(T){}
};
int main(){
S a;
S b(a);
}
请注意,唯一真正的区别是复制初始化是显式的,而不是包含在函数调用中。顺便说一下,Clang接受了这个程序

其次,涉及复制构造函数并不是这个问题的根本(C++11中的规则12.8p6);以下是gcc(所有版本)拒绝并接受的另一个类似程序:

struct S {};

template<typename, typename> struct U {};
template<typename T> struct U<S, T> {};
template<typename T> struct U<T, S> {};

void f(S);
template<typename T> typename U<S, T>::type f(T);

int main() {   
  S a;
  f(a);
}
struct S{};
模板结构U{};
模板结构U{};
模板结构U{};
无效f(S);
模板类型名称U::类型f(T);
int main(){
S a;
f(a);
}
clang和gcc的区别在于14.8.2p8的应用:

[…][注:对替换类型和表达式的求值可能会产生副作用,如类模板专门化和/或函数模板专门化的实例化、隐式定义函数的生成等。此类副作用不在“即时上下文”中并可能导致程序格式错误。-结束注释]

模板专用化
歧义
中的歧义在直接上下文之外,因此程序的格式不正确。(支持这一点的论点是,模板专门化模糊性不会出现在随后的类型推断失败原因列表中)

MSVC又不同了;它接受clang和gcc都拒绝的以下程序:

template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T, typename = typename U<T>::type> S(T) {}
};

int main() {
    S a;
    S b(a);
}
模板结构U{typedef typename T::type type;};
结构{
S()=默认值;
模板S(T){}
};
int main(){
S a;
S b(a);
}
这取决于规则12.8p6:

如果类X的构造函数声明的第一个参数类型为(可选cv限定)X,并且没有其他参数,或者所有其他参数都有默认参数,则该类X的构造函数声明的格式是错误的。成员函数模板从未实例化以生成这样的构造函数签名

然而,为了确定成员函数模板实例化是否是关于12.8p6的构造错误,有必要实例化其声明(参见14.7.1p9)。请注意,MSVC拒绝以下程序,因此它甚至不一致:

template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T> S(T, typename U<T>::type *p = 0) {}
};

int main() {
    S a;
    S b(a);
}
模板结构U{typedef typename T::type type;};
结构{
S()=默认值;
模板S(T,typename U::type*p=0){}
};
int main(){
S a;
S b(a);
}
这有一些非常有趣的行为效果;MSVC接受以下(格式错误)程序:

模板结构U{typedef typename T::type type;};
结构{
S()=默认值;
模板S(T){}
};
模板类型名U::类型f(T){返回0;}
int main(){
S a;
sb(a);//XXX
f(a);
}

但是,如果复制初始化
sb(a)
被注释掉,程序将被拒绝

回答得好!还有一个问题:在14.8.2p8的应用程序中,GCC和clang之间的区别是因为clang不一定实例化所有的模板专门化,或者它将潜在的格式错误的实例化视为处于“直接上下文”(因此不会导致整个程序格式错误)?@DaviD。后者-如果我们引入一个中间的
模板结构V:U{}
然后clang拒绝该程序,因此它肯定是在实例化该模板专门化。
template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T> S(T, typename U<T>::type *p = 0) {}
};

int main() {
    S a;
    S b(a);
}
template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T, typename = typename U<T>::type> S(T) {}
};
template<typename T> typename U<T>::type f(T) { return 0; }

int main() {
    S a;
    S b(a);  // XXX
    f(a);
}