4

次のコードは私の問題を要約しています:

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

サンプルコードのように明示的なコンストラクターが既にあることを考慮して、テンプレートパラメーターに関係なく、現在のクラスを除いて、親クラスから派生したすべてのクラスに暗黙的なコンストラクターを制限する方法は?

編集: Base からの暗黙のコンストラクターについては、明らかに次のように記述できます。

template<class OtherParameter> Derived2(const Base<OtherParameter>& x);

しかし、その場合、コンパイラがこのコンストラクタを暗黙のコンストラクタとして使用しないという保証はありDerived2<OtherParameter1, OtherParameter2, OtherParameter>ますか?

EDIT2:ここにテストがあります:(ここの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;
}

最後の行が気になります。C++標準で大丈夫ですか?g++ のバグですか?

4

3 に答える 3

6

参照している各コンストラクターはテンプレート化されたクラス メソッドであるため、テンプレートのインスタンス化と関数のオーバーロード解決の規則が呼び出されます。

C++11 標準のセクション 14.8.3 を見ると、実際には段落 1 ~ 3 にいくつかの例があり、あなたの質問をいくらか示しています。基本的に、C++ コンパイラは、オーバーロードされた一連のテンプレート関数 (必要に応じて型の変換が追加されます) の中から、最も一致する、または「最も一般化されていない」テンプレート関数のインスタンス化を探します。あなたの場合、オブジェクトの代替インスタンス化を取るコンストラクターを明示的に作成したため、そのコンストラクターは、ジェネリック型または引数を取るものと比較してDerived2、任意の型の優先オーバーロードになります。Derived2<...>TBase<OtherParameter>

UPDATE : どうやら、C++11 標準の 12.3.1/2 によると、

明示的なコンストラクターは、非明示的なコンストラクターと同様にオブジェクトを構築しますが、直接初期化構文 (8.5) またはキャスト (5.2.9、5.4) が明示的に使用されている場合にのみ構築します。

つまり、オブジェクトの構築に直接初期化構文を使用しない場合、またはキャストを選択しない場合は、 としてマークされているコンストラクターを使用できないということですexplicit。これは、テスト #9 と #10 の間に見られる不可解な結果を説明しています。

于 2012-08-29T19:55:23.693 に答える
2

型が の特殊化であるかどうかを報告するトレイトを書くことができますDerived2<>:

template<typename T>
struct is_derived2 : std::false_type { };

template<class P1, class P2, class P>
struct is_derived2<Derived2<P1, P2, P>> : std::true_type { };

そして、Pinを抽出する関数スタブBase<P>:

template<typename Parameter>
Parameter base_parameter(Base<Parameter> const&);

次に、暗黙のコンストラクターを次のように変更します。

template<
    class T,
    class = typename std::enable_if<
        !is_derived2<T>::value
        && std::is_base_of<
            Base<decltype(base_parameter(std::declval<T>()))>,
            T
        >::value
    >::type
>
Derived2(const T& x);

オンライン デモ: http://liveworkspace.org/code/c43d656d60f85b8b9d55d8e3c4812e2b


更新: これらの変更を「編集 2」リンクに組み込んだオンライン デモを次に示します

于 2012-08-29T19:52:48.083 に答える
0

わかりました、多分私は「偽の」コンストラクターの追加を意味するだけの回避策を見つけました:

#include <iostream>
#include <type_traits>
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 Something> Derived(const Something& x) {std::cout<<"implicit"<<std::endl;}

    // Workaround
    public:
        template<template<typename> class Something, typename OtherType,
        class = typename std::enable_if< std::is_same< Something<OtherType>, Derived<OtherType> >::value>::type >
        Derived(const Something<OtherType>& x)
        {std::cout<<"workaround (for example always false static assert here)"<<std::endl;}
};
template<unsigned int Size> class Test {};

int main()
{
    Other<int> other0;
    Other<double> other1;
    Test<3> test;
    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;                        // <- workaround
    std::cout<<"11 = ";
    ddouble = test;                         // <- implicit
    return 0;
}

@Everybody:それはその問題の良い解決策だと思いますか?

LWS: http: //liveworkspace.org/code/f581356a7472c902b10ca486d648fafc

于 2012-08-29T21:30:25.097 に答える