16

タイプリストの各クラスにメソッドを提供する可変個引数テンプレート クラスを作成しようとしています。printタイプリスト内のすべてのクラスのメソッドを作成する例を以下に示します。

#include <iostream>
#include <string>

// Helper class providing a function call
template <typename T>
class PrintHelper
{
public:
    void print(const T& t) { std::cout << t << std::endl; }
};

// Provides a print method for each type listed
template <typename... Ts>
class Printer : public PrintHelper<Ts>...
{};

int main()
{
    Printer<int, std::string> p;
    p.print(std::string("Hello World")); // Ambiguous Call
}

コメント行は、コメント行で GCC 4.6.3 からのエラーになります。あいまいさを解決する正しい方法は何ですか、または別の設計を検討する必要がありますか?

4

3 に答える 3

10

あいまいさを解決するために、次のことが可能です

template <typename... Ts>
struct Printer : PrintHelper<Ts>...
{
    template <typename U>
    void print (const U& t)
    {
        PrintHelper<U>::print (t);
    }
};

(を参照)

しかし、これは期待するほど堅牢ではありません。特に、型リストのいずれかの型に変換可能なオブジェクトを印刷することはできません。

ただし、一部のテンプレート メタプログラミングを使用すると、正しいプリンターにディスパッチすることができます。これを行うTs...Uは、変換可能な型を選択し、右を呼び出す必要がありますPrintHelper

PrintHelper<typename find_convertible<U, Ts...>::type>::print (t);

find_convertible<U, Ts...>によって定義される場所

template <typename U, typename... Ts>
struct find_convertible
{};

template <typename U, typename V, typename... Ts>
struct find_convertible<U, V, Ts...> :
    std::conditional<
        std::is_convertible<U, V>::value, 
        std::common_type<V>, // Aka identity
        find_convertible<U, Ts...>
    >::type
{};

(を参照)

于 2012-12-13T17:34:42.827 に答える
8

自分の質問に答えるのは好きではありませんが、ここのコメントから次のソリューションを作成しました。すべてのprint関数をスコープに入れ、すべての関数で C++ オーバーロードの解決を可能にします。

#include <iostream>
#include <string>

// Helper class providing a function call
template <typename T>
class PrintHelper
{
public:
    void print(const T& t) { std::cout << t << std::endl; }
};

// Provides a print method for each type listed
template <typename... Ts>
class Printer
{};

template<typename T>
class Printer<T> : public PrintHelper<T>
{
public:
    using PrintHelper<T>::print;
};

template<typename T, typename... Ts>
class Printer<T, Ts...>: public PrintHelper<T>, public Printer<Ts...>
{
public:
    using PrintHelper<T>::print;
    using Printer<Ts...>::print;
};

int main()
{
    Printer<int, std::string> p;
    p.print("Hello World"); // Not an ambiguous Call
}
于 2012-12-13T21:10:22.257 に答える
3

次のコードは、あいまいさの問題を解決できます。

#include <iostream>
#include <string>

// Helper class providing a function call
template <typename T>
class PrintHelper
{
  protected:
    void print_impl(const T& t) { std::cout << t << std::endl; }
};

// Provides a print method for each type listed
template <typename... Ts>
class Printer : public PrintHelper<Ts>...
{
  public:
    template <typename U>
    void print(const U& u) { 
      PrintHelper<U>::print_impl(u); 
    };
};

int main()
{
    Printer<int, std::string> p;
    p.print(std::string("Hello World")); // Ambiguous Call
}

タイプU(呼び出し時に推定される)が可変個引数タイプリストのタイプの1つである必要があるため、これはあまり良くありません。あなたはその問題を解決するために少し物事を空想することができるかもしれません。テンプレートの魔法とSfinaeを少し使用すれば、おそらくこれをかなり簡単に解決できます(ただし、それほどきれいではありません)。

あいまいさの問題は、テンプレートや可変個引数テンプレートの使用とは関係ありません。もちろん、メンバールックアップルール(標準のセクション10.2 / 2)、つまり、よく呼ばれる「メンバー非表示ルール」を直接適用したものです。 "。この単純な非テンプレートバージョンを使用すると、同じあいまいさの問題が発生しますが、非常に単純な解決策があります。

struct IntPrinter {
  void print(const int& i) { std::cout << i << std::endl; };
};

struct StringPrinter {
  void print(const std::string& s) { std::cout << s << std::endl; };
};

struct IntStringPrinter : IntPrinter, StringPrinter {
  using IntPrinter::print;       // These using-statements will solve the problem
  using StringPrinter::print;    // by importing all 'print' functions to the same 
                                 // overload resolution level.
};

したがって、ここでの問題は、コンパイラが通常のオーバーロード解決ルールを適用しようとする前にあいまいさが生じることです。これは、最初に継承のどのブランチをたどってメンバー関数を見つけようとするためです。 1つの継承レベルでのみ過負荷を解決します。また、可変個引数テンプレートを使用する場合の問題は、一連の「using」ステートメントを解凍して、すべての印刷関数を同じ継承レベルまでインポートする方法がないように見えることです。そのような使用ステートメントを解凍する方法を誰かが知っているなら、私はすべての耳です。可変個引数テンプレート以前のソリューション(10個の引数を持つ一般的なテンプレートのように、10個すべてのバージョンに特化する)にフォールバックする必要がある場合があります。

于 2012-12-13T17:47:23.437 に答える