3

タイトルが意味をなすことを願っています。正確に表現するための語彙が欠けているのでしょう。

まあ、例はおそらくより明確になるでしょう。

私の問題は次のとおりです。動的ダウンキャストは、次のケースのいくつかで実行時に0を返します(コメントに書かれています)。それが正しい動作 (C++11 を使用) であるかどうか、またその理由と、それを機能させるために何ができるかを知りたいです。明らかに、Templated と A::A_templated は、エイリアス "using" を使用して同一として定義されているにもかかわらず、異なるクラスとして扱われます。単純な typedef エイリアスでは問題は発生しません。

template <class T>
class Templated {};

class A {
    public :
    typedef int A_Type;
    template <class T>
    using A_Templated = Templated<T>;
};

class Test_base {
    public :
    Test_base() {}
    virtual void foo()=0;
};

template <class T>
class Test_Type : public Test_base {
    public :
    Test_Type() {}
    void foo() {}
};

template < template <class T> class TT >
class Test_Templated : public Test_base {
    public :
    Test_Templated() {}
    void foo() {}
};

int main() {
    Test_base* test;

    test = new Test_Type<int>;
    std::cout << dynamic_cast< Test_Type<int>* >(test) << std::endl;//-->ok
    std::cout << dynamic_cast< Test_Type<A::A_Type>* >(test) << std::endl;//-->ok

    test = new Test_Templated<Templated>;
    std::cout << dynamic_cast< Test_Templated<Templated>* >(test) << std::endl;//-->ok
    std::cout << dynamic_cast< Test_Templated<A::A_Templated>* >(test) << std::endl;//--> returns 0 !

    test = new Test_Templated<A::A_Templated>;
    std::cout << dynamic_cast< Test_Templated<A::A_Templated>* >(test) << std::endl;//-->ok
    std::cout << dynamic_cast< Test_Templated<Templated>* >(test) << std::endl;//--> returns 0 !


}

問題を確認する別の方法を提案します。これはおそらくより明確です。上記の例を回避しようとした後、私はそれに直面しています。次の例は、基本的に Bogdan が指摘したことを示しています。コンパイラが Templated_alias を使用して Templated を解決できないという事実は、非常にイライラさせられます。テンプレートエイリアスを介して強制的に型を解決できるコンパイルオプションが存在するかどうか疑問に思っています。

template <class T>
class Templated {};

template <class T>
using Templated_alias = Templated<T>;

template < template <class T> class TT >
class B;

template <>
class B<Templated> {
    public :
    void foo(Templated<int> _arg) {}
};

int main() {
    B<Templated> b1;
    b1.foo(Templated<int>());
    b1.foo(Templated_alias<int>());//compiles => Templated_alias<int> is equivalent to Templated<int>
    B<Templated_alias> b2;//Compilation error: Implicit instantiation of undefined template B<Templated_alias>
    //which means: Templated_alias is not equivalent to Templated
}

ボグダンのトリックのおかげで、少し鼻血が出た後、何とか解決策を見つけることができました. アイデアは、テンプレート クラスの潜在的なエイリアスの「フィルタリング」を担当するクラスを構築することです。「フィルタリング」する必要があるテンプレート クラスごとに 1 つの仕様が必要です。この方法の主な欠点は、一貫性を保つために、テンプレート クラスがテンプレート パラメータとして使用されるすべての場所でフィルタリングを使用する必要があることです。

//Classes to be dealt with

template <class T>
class Templated {};

template <class T>
class Templated2 {};

template <class T>
using Templated_alias = Templated<T>;

class A_base {
    virtual void foo()=0;
};

template <template <class T> class TT>
class A : public A_base {
    void foo() {}
};

//Here starts the trick definition

template<template<class> class TT1, template<class> class TT2>
using is_same_template_t = typename std::is_same<TT1<int>, TT2<int> >::type;

//Template Template aliasing
template < template <class T> class TT >
class TT_aliasing {
    public :
    template <class T>
    using Class_T = TT<T>;
};

//Template Template Alias Filtering
template < template <class T> class TT, class = std::true_type>
class TT_AF {
    public :
    template <class T>
    using Class_T = TT<T>;
};

template < template <class T> class TT >
class TT_AF<TT, is_same_template_t<TT, Templated> > : public TT_aliasing<Templated> {};

int main() {

    A_base* a;
    a = new A< TT_AF<Templated>::Class_T >();
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl;

    std::cout << "---------------" << std::endl;

    a = new A< TT_AF<Templated_alias>::Class_T >();
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl;

    std::cout << "---------------" << std::endl;

    a = new A< TT_AF<Templated2>::Class_T >();
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl;
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl;

    A< TT_AF<Templated>::Class_T > a1;
    A< TT_AF<Templated_alias>::Class_T > a2;
    a1 = a2;
    A< TT_AF<Templated2>::Class_T > a3;
    //a1 = a3;//no viable overloaded '='

}

出力は次のとおりです。

0x600000014ba0
0x600000014ba0
0x0
---------------
0x600000014bb0
0x600000014bb0
0x0
---------------
0x0
0x0
0x600000014bc0

上記のトリックを使用した後。私はさまざまな問題に遭遇しました。絶対に関係があるとは断言できませんが、その可能性は非常に高いです。コンパイラは「動的テーブル」を正しく構築するのに苦労しているようです。type_info::hash_code を 2 つの (おそらく) 同じオブジェクトで異なるものにすることができる C++でこの問題を求めました 。

4

1 に答える 1

5

Clang の動作は正しいです。

A::A_Typeint標準の [7.1.3p1] によると同等です。

[...] その宣言の範囲内では、typedef-nameは構文的にキーワードと同等であり、8 節で説明されている方法で識別子に関連付けられた型に名前を付けます。したがって、 typedef-nameは別の型の同義語です。typedef-nameは、クラス宣言 (9.1) または enum 宣言のように新しい型を導入しません。

A::A_Templated<int>Templated<int>[14.5.7p2]によると同等です:

template-idがエイリアス テンプレートの特殊化を参照する場合、エイリアス テンプレートのtype-idのtemplate-parametersをtemplate-argumentsに置き換えることによって取得される関連する型と同等です。

ただし、[14.5.7p1] によると、 と同等でA::A_Templatedはありません。Templated

[...] エイリアス テンプレートの名前はtemplate-nameです。

これは、A::A_TemplatedTemplatedが 2 つの異なるテンプレートであることを意味します。つまり、Test_Templated<A::A_Templated>Test_Templated<Templated>は の異なる特殊化ですTest_Templated。したがって、null ポインターを返すキャストは正しいことを意味します。

GCC 5.1.0 はこれを正しく処理しません。Clang 3.6.0 と MSVC 14 RC では正しく処理されます。


すべての参照はワーキング ドラフト N4431 です。

この動作に関するアクティブなコア ワーキング グループの問題があることに注意してください -問題 1286。著者は、標準的な表現を導入して、そのようなケースが期待どおりに機能するようにすること、つまり、別名テンプレートをtype-idで参照されるものと同等にすることを意図していると述べています。そこには 2015 年 5 月のメモがあり、この問題が注目されていることを示していますが、まだそこにはありません。


「機能させる」という点では、実際のニーズが何であるかを知らずに解決策を提供することは困難ですが、テンプレート自体ではなく、のTest_Templated専門化に依存するようにしようと思います。つまり、次のように宣言しますTemplated

template<class T>
class Test_Templated : public Test_base { /* ... */ };

そしてそれを次のように使用します

test = new Test_Templated<Templated<int>>;
std::cout << dynamic_cast< Test_Templated<Templated<int>>* >(test) << std::endl; //ok
std::cout << dynamic_cast< Test_Templated<A::A_Templated<int>>* >(test) << std::endl; //also ok

何らかの方法で役立つ場合は、間接的なレベルを追加することでこれをラップできます。

template<template<class> class TT, class T> using Make_Test_Templated = Test_Templated<TT<T>>;

そして、次のように使用します。

test = new Make_Test_Templated<A::A_Templated, long>;
std::cout << dynamic_cast< Make_Test_Templated<A::A_Templated, long>* >(test) << std::endl; //ok
std::cout << dynamic_cast< Make_Test_Templated<Templated, long>* >(test) << std::endl; //also ok

とにかく、専門分野が同等であるという事実を利用することが重要だと思います。


よし、最新の更新に基づいて、2 番目のコード サンプルの問題に対処するハックを次に示します。明示的な特殊化を、特定の引数でインスタンス化されたときB<Templated>と同じ特殊化を生成するテンプレートが与えられた場合にのみ一致する部分的な特殊化に変更します (たとえば、この例では)。Templatedint

紛らわしい文はどうですか?ごめん。上記の変更により、コード サンプルは次のようになります。

#include <iostream>
#include <type_traits>

template<class> class Templated { };
template<class T> using Templated_alias = Templated<T>;
template<class> class Templated2 { };

// Helper trait
template<template<class> class TT1, template<class> class TT2>
using is_same_template_t = typename std::is_same<TT1<int>, TT2<int>>::type;

template<template<class> class, class = std::true_type> class B;
template<template<class> class TT> class B<TT, is_same_template_t<TT, Templated>>
{
public:
   void foo(Templated<int>) { std::cout << "B<Templated>::foo\n"; }
};

int main() {
   B<Templated> b1;
   b1.foo(Templated<int>());
   b1.foo(Templated_alias<int>());
   B<Templated_alias> b2; // Works fine now, and so do the next two lines.
   b2.foo(Templated<int>());
   b2.foo(Templated_alias<int>());
   // B<Templated2> b22; // Error trying to instantiate the primary template B.
}

引数is_same_template_tでインスタンス化できるテンプレートをチェックするためにのみ使用されることを確認する必要があることに注意してください(もちろん、必要なものに変更してください)。より一般的なものにしたい場合は、次のように、テンプレートをインスタンス化する必要がある型をトレイトのパラメーター リストに含めることもできます。intint

template<template<class> class TT1, template<class> class TT2, class T>
using is_same_template_t = typename std::is_same<TT1<T>, TT2<T>>::type;

次のように使用します。

is_same_template_t<TT, Templated, int>
于 2015-06-04T21:27:09.230 に答える