2014-05-19 125 views
8

在以下示例中,GCC >= 4.7实例化模板构造函数(可通过读取错误消息来观察),但应仅需要隐式生成的复制构造函数。在需要复制构造函数时实例化GCC:模板构造函数

#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); 
} 

以上代码编译在GCC < 4.7clangMSVS(不知道哪个版本,我使用的http://rextester.com/runcode之一)。在GCC >= 4.7编译失败,出现以下消息:调用b.set(a);

main.cpp: In substitution of ‘template<class T, class> quantity<Type>::quantity(quantity<T>) [with T = int; <template-parameter-1-2> = <missing>]’: 
main.cpp:39:12: required from here 
main.cpp:23:9: error: ambiguous class template instantiation for ‘struct ambiguous<int, int>’ 
     typename = typename std::enable_if<ambiguous<Type, T>::value>::type 
     ^
main.cpp:9:8: error: candidates are: struct ambiguous<int, T> 
struct ambiguous<int, T> : std::true_type {}; 
     ^
main.cpp:12:8: error:     struct ambiguous<T, int> 
struct ambiguous<T, int> : std::true_type {}; 
     ^
main.cpp: In function ‘int main()’: 
main.cpp:31:10: error: initializing argument 1 of ‘void quantity<Type>::set(quantity<T>) [with T = int; <template-parameter-2-2> = void; Type = float]’ 
    void set(quantity<T>) {} 

所以,GCC显然是寻找一个拷贝构造函数,在路上,实例化模板的构造函数这反过来实例ambiguous<int, int>这是(呃... ...)暧昧。

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

回答

6

gcc是正确的。

有几个问题在这里不幸的是已经在你的问题变得混为一谈:

首先,GCC < 4.7的行为并无本质区别;因为(至少)4.4 GCC的所有版本的拒绝非常相似的程序:

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); 
} 

注意,唯一的区别是,拷贝初始化是显式的,而不是包含在一个函数调用。顺便说一句,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); 
} 

铛和gcc之间的区别是在14.8.2p8应用:

[......] [注:替代类型和表达式的评估会导致副作用,例如类模板专业化和/或功能模板专业化的实例化,隐式定义函数的生成等。这些副作用不在“直接上下文”中,并可能导致程序不合格。 - 结束注释]

模板专业化ambiguous<int, int>的歧义性在直接上下文之外,所以程序不合格。 (对此的一个支持论点是模板专门化歧义不会出现在类型推导失败的后续列表中)。

MSVC又不同了;它接受铛和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); 
} 

这归结于规则12。8p6:

一类X构造的声明是非法的构造,如果它的第一个参数是类型(任选CV-合格)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); 
} 

这有一些非常有趣的行为影响; MSVC接受以下(病态的)程序:

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); 
} 

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

+0

很好的回答!还有一个问题:在14.8.2p8的应用中,GCC和clang之间的区别是clang不一定实例化所有模板专业化,或者它将潜在的不合格实例作为“直接上下文”来处理(因此不会导致整个程序不合格)? –

+0

@DaviD。后者 - 如果我们引入一个中间模板 struct V:U {};那么clang会拒绝该程序,所以它肯定会实例化该模板特化。 – ecatmur