从Base派生的所有类型可用的隐式构造函数除了当前类型?

Vin*_*ent 4 c++ explicit-constructor implicit-conversion enable-if c++11

以下代码总结了我的问题:

template<class Parameter>
class Base {};

template<class Parameter1, class Parameter2, class Parameter>
class Derived1 : public Base<Parameter>
{ };

template<class Parameter1, class Parameter2, class Parameter>
class Derived2 : public Base<Parameter>
{
public :
    // Copy constructor
    Derived2(const Derived2& x);

    // An EXPLICIT constructor that does a special conversion for a Derived2
    // with other template parameters
    template<class OtherParameter1, class OtherParameter2, class OtherParameter>
    explicit Derived2(
        const Derived2<OtherParameter1, OtherParameter2, OtherParameter>& x
    );

    // Now the problem : I want an IMPLICIT constructor that will work for every
    // type derived from Base EXCEPT
    // Derived2<OtherParameter1, OtherParameter2, OtherParameter> 
    template<class Type, class = typename std::enable_if</* SOMETHING */>::type>
    Derived2(const Type& x);
};
Run Code Online (Sandbox Code Playgroud)

如何将隐式构造函数限制为从父类派生的所有类,除了它的模板参数之外的当前类,考虑到我已经在示例代码中有一个显式的构造函数?

编辑:对于Base的隐式构造函数,我显然可以写:

template<class OtherParameter> Derived2(const Base<OtherParameter>& x);
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我是否保证编译器不会将此构造函数用作隐式构造函数Derived2<OtherParameter1, OtherParameter2, OtherParameter>

编辑2:这里我有一个测试:( LWS在这里:http://liveworkspace.org/code/cd423fb44fb4c97bc3b843732d837abc)

#include <iostream>
template<typename Type> class Base {};
template<typename Type> class Other : public Base<Type> {};
template<typename Type> class Derived : public Base<Type>
{
    public:
        Derived() {std::cout<<"empty"<<std::endl;}
        Derived(const Derived<Type>& x) {std::cout<<"copy"<<std::endl;}
        template<typename OtherType> explicit Derived(const Derived<OtherType>& x) {std::cout<<"explicit"<<std::endl;}
        template<typename OtherType> Derived(const Base<OtherType>& x) {std::cout<<"implicit"<<std::endl;}
};
int main()
{
    Other<int> other0;
    Other<double> other1;
    std::cout<<"1 = ";
    Derived<int> dint1;                     // <- empty
    std::cout<<"2 = ";
    Derived<int> dint2;                     // <- empty
    std::cout<<"3 = ";
    Derived<double> ddouble;                // <- empty
    std::cout<<"4 = ";
    Derived<double> ddouble1(ddouble);      // <- copy
    std::cout<<"5 = ";
    Derived<double> ddouble2(dint1);        // <- explicit
    std::cout<<"6 = ";
    ddouble = other0;                       // <- implicit
    std::cout<<"7 = ";
    ddouble = other1;                       // <- implicit
    std::cout<<"8 = ";
    ddouble = ddouble2;                     // <- nothing (normal : default assignment)
    std::cout<<"\n9 = ";
    ddouble = Derived<double>(dint1);       // <- explicit
    std::cout<<"10 = ";
    ddouble = dint2;                        // <- implicit : WHY ?!?!
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

最后一行让我担心.C++标准可以吗?这是g ++的错误吗?

Jas*_*son 6

由于您引用的每个构造函数都是模板化类方法,因此会调用模板实例化和函数重载决策的规则.

如果你看一下C++ 11标准的第14.8.3节,实际上第1-3段中有一些例子可以说明你的问题.基本上,C++编译器将在一系列重载模板函数中寻找最佳匹配或"最不通用"模板函数实例化(如果需要,可以添加类型转换).在您的情况下,因为您已经显式创建了一个构造函数,该构造函数接受Derived2对象的替代实例化,所以Derived2<...>与采用泛型类型T甚至Base<OtherParameter>参数的构造函数相比,该构造函数将是任何类型的首选重载.

更新:显然,根据C++ 11标准中的12.3.1/2,

显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法(8.5)或强制转换(5.2.9,5.4)的情况下才这样做.

这意味着如果您不使用直接初始化语法来构造对象或选择强制转换,那么您就不能使用任何标记为的构造函数explicit.这解释了你在测试#9和#10之间看到的令人费解的结果.