2

可変個引数テンプレート引数を単純なテンプレート引数と一緒に使用すると、バインドされたファンクターからインスタンス化されたときにis_base_ofの奇妙な動作が発生しまし

コードは次のとおりです。

template <class T, class Index>
class Base{};

template<typename T>
struct Checker {
    typedef int result_type;

    // Returns 1 if a given T type is descendant of Base<T,First>
    template<typename First, typename ...Args>
    result_type operator()(First&& first, Args&&... params)
    {
        return check(std::is_base_of<Base<T,First>, T>(),
                std::forward<First>(first),
                std::forward<Args>(params)...);
    }
    template<typename ...Args>
    result_type check(const std::true_type&, Args&&... params)
    {
        return 1;
    }
    template<typename ...Args>
    result_type check(const std::false_type&, Args&&... params)
    {
        return 0;
    }
};

struct A {};
struct B : Base<B,int> {};

int main()
{
    Checker<A> ch1;
    std::cout<<ch1(3.14)<<std::endl;
    Checker<B> ch2;
    std::cout<<ch2(1 ,3.14)<<std::endl; // output is 1
    std::cout<<std::bind(ch2, 1, 3.14)()<<std::endl; // output is 0 but it should be 1 !
    return 0;
}

プログラムの出力は次のとおりです。

0
1
0

しかし、私は期待します:

0
1
1

可変個引数テンプレートを間違った方法で使用していますか?Argsのような可変個引数型リストの最初の型を取得する他の(正しい)方法はありますか?バインド式で使用した場合にのみ問題になるのはなぜですか?

基本テンプレートを変更してテンプレートパラメータを1つだけにする場合、バインド式は機能することに注意してください。

template <class T>
class Base{};

template<typename T>
struct Checker {
    typedef int result_type;

    // Returns 1 if a given T type is descendant of Base<T>
    template<typename ...Args>
    result_type operator()(Args&&... params)
    {
        return check(std::is_base_of<Base<T>, T>(),
                std::forward<Args>(params)...);
    }
    template<typename ...Args>
    result_type check(const std::true_type&, Args&&... params)
    {
        return 1;
    }
    template<typename ...Args>
    result_type check(const std::false_type&, Args&&... params)
    {
        return 0;
    }
};

struct A {};
struct B : Base<B> {};

int main()
{
    Checker<A> ch1;
    std::cout<<ch1(3.14)<<std::endl;
    Checker<B> ch2;
    std::cout<<ch2(3.14)<<std::endl; // output is 1
    std::cout<<std::bind(ch2, 3.14)()<<std::endl; // output is 1 this time!
    return 0;
}
4

2 に答える 2

2

後に呼び出されたときの関数オブジェクトのデータ型はFirst、ではなく、型であるため、期待どおりの出力が得られません。 Checkerstd::bind()int&int

したがって、への呼び出しに対してをstd::is_base_of<Base<B,int&>, B>インスタンス化しません。std::true_typeChecker::check

問題は、std::bind渡す関数の引数を内部的に格納するオブジェクトを作成することです。std::bindしたがって、関数にバインドするための引数として渡した値を保持している、によって返されるオブジェクトの非静的データメンバーとして名前付きのl値があります。ファンクターのを呼び出したときにその非静的データメンバーがr値参照にoperator()渡されると、一時オブジェクトではなくなったため、l値参照として渡されます。次のようなことをした場合にも、同様の問題が発生します。

int x = 1;
Checker<B> ch2;
std::cout<<ch2(x, 3.14)<<std::endl;

名前付き値はl値であり、r値参照であるため、一時的ではなくl値参照としてメソッドの引数にx渡されます。したがって、タイプは再びではなくとして終了し、値を出力します。firstoperator()firstint&int0

この問題を解決するには、次のようにします。

template<typename First, typename ...Args>
result_type operator()(First&& first, Args&&... params)
{
   if (std::is_reference<First>::value)
    {
        return check(std::is_base_of<Base<T, typename std::remove_reference<First>::type>, T>(),
            std::forward<First>(first),
            std::forward<Args>(params)...);
    }
    else
    {
        return check(std::is_base_of<Base<T,First>, T>(),
            std::forward<First>(first),
            std::forward<Args>(params)...);
    }
}

これにより、オブジェクトの参照タイプが削除され、必要な結果が得られます。

于 2011-08-19T16:25:23.190 に答える
0

残念ながら、std :: is_referenceは、より複雑な問題で期待される結果を提供しませんでした。したがって、最後に、参照とconst-referenceのオーバーロードを提供することを選択しました。

template<typename First, typename ...Args>
result_type operator()(First& first, Args&&... params)
{
    return check(std::is_base_of<Base<T,First>, T>(),
            first,
            std::forward<Args>(params)...);
}
template<typename First, typename ...Args>
result_type operator()(const First& first, Args&&... params)
{
    return check(std::is_base_of<Base<T,First>, T>(),
            first,
            std::forward<Args>(params)...);
}
于 2011-08-21T00:15:59.570 に答える