2

次のコードとapply関数を考えてみましょう:

// Include
#include <iostream>
#include <array>
#include <type_traits>
#include <sstream>
#include <string>
#include <cmath>
#include <algorithm>

// Just a small class to illustrate my question
template <typename Type, unsigned int Size>
class Array
{
    // Class body (forget that, this is just for the example)
    public:
        template <class... Args> Array(const Args&... args) : _data({{args...}}) {;}
        inline Type& operator[](unsigned int i) {return _data[i];}
        inline const Type& operator[](unsigned int i) const {return _data[i];}
        inline std::string str() 
        { 
            std::ostringstream oss; 
            for (unsigned int i = 0; i < Size; ++i) 
                oss<<((*this)[i])<<" "; 
            return oss.str();
        }
    protected:
        std::array<Type, Size> _data;
    // Apply declaration
    public:
        template <typename Return, 
                  typename SameType, 
                  class... Args, 
                  class = typename std::enable_if<std::is_same<typename std::decay<SameType>::type, Type>::value>::type> 
        inline Array<Return, Size> apply(Return (*f)(SameType&&, Args&&...), const Array<Args, Size>&... args) const;
}; 

// Apply definition
template <typename Type, unsigned int Size>
template <typename Return, typename SameType, class... Args, class> 
inline Array<Return, Size> Array<Type, Size>::
apply(Return (*f)(SameType&&, Args&&...), const Array<Args, Size>&... args) const
{
    Array<Return, Size> result; 
    for (unsigned int i = 0; i < Size; ++i) {
        result[i] = f((*this)[i], args[i]...);
    }
    return result;
}

// Example
int main(int argc, char *argv[])
{
    Array<int, 3> x(1, 2, 3);
    std::cout<<x.str()<<std::endl;
    std::cout<<x.apply(std::sin).str()<<std::endl;
    return 0;
}

コンパイルは次のエラーで失敗します:

universalref.cpp: In function ‘int main(int, char**)’:
universalref.cpp:45:32: erreur: no matching function for call to ‘Array<int, 3u>::apply(<unresolved overloaded function type>)’
universalref.cpp:45:32: note: candidate is:
universalref.cpp:24:200: note: template<class Return, class SameType, class ... Args, class> Array<Return, Size> Array::apply(Return (*)(SameType&&, Args&& ...), const Array<Args, Size>& ...) const [with Return = Return; SameType = SameType; Args = {Args ...}; <template-parameter-2-4> = <template-parameter-1-4>; Type = int; unsigned int Size = 3u]
universalref.cpp:24:200: note:   template argument deduction/substitution failed:
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘long double’
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘float’
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘double’
universalref.cpp:45:32: note:   couldn't deduce template parameter ‘Return’

なぜ失敗するのかよくわかりません。そのコードでは、次のことを希望します。

  • 最も一般的な機能を持つようにユニバーサル参照を保持する
  • 構文を使用できるようにするapply(std::sin)

では、コンパイルするには何を変更する必要がありますか?

4

1 に答える 1

1

functionにはいくつかのオーバーロードが存在し、関数テンプレートには完全に一致std::sinするものを推測する方法がありません(ほとんどの場合、テンプレート引数を推測するときに変換は考慮されません)。

まず第一に、これらのオーバーラッドはどれも最初の (そして唯一の) 引数への参照を受け入れません。コンパイラはこれを伝えているのでReturnSameType引数のタイプから推測することはできませんf:

universalref.cpp:24:200: note:   template argument deduction/substitution failed:
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘long double’
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘float’
universalref.cpp:45:32: note:   mismatched types ‘SameType&&’ and ‘double’

したがって、最初のステップは、apply()関数テンプレートの署名を変更することです。

[...] apply(Return (*f)(SameType, Args&&...), [...]
                        ^^^^^^^^
                        No URef!

さらに、SFINAE は、Returnが推定されない関数テンプレートのすべてのインスタンス化を排除しています(のインスタンスをint呼び出しています): 残念ながら、既存の のオーバーロードには戻り値の型としてありません。apply()Array<int, 3>std::sinint

インスタンス化を に変更してみることができますArray<double, 3>が、C++11 標準のパラグラフ 14.8.2/5 が次のように述べているため、残念ながらそれだけでは役に立ちません。

非推定コンテキストは次のとおりです。

— 修飾 ID を使用して指定されたタイプのネストされた名前指定子。

— 非型のテンプレート引数、または部分式がテンプレート パラメーターを参照する配列バインド。

— 引数推定が行われている呼び出しで使用されている既定の引数を持つ関数パラメーターのパラメーター型で使用されるテンプレート パラメーター。

—関連付けられた関数引数が関数、またはオーバーロードされた関数のセット(13.4) であり、次の 1 つ以上が適用されるため、引数推定を実行できない関数パラメーター:

— 複数の関数が関数パラメーターの型と一致する (あいまいな推論になる)、または

— 関数パラメーターの型に一致する関数がない、または

引数として提供される関数のセットには、1 つ以上の関数テンプレートが含まれます

整数型のサポートが必要なオーバーロードのため(これは C+11 の新機能です。26.8/11 を参照してください)、ライブラリの実装でのテンプレート オーバーロードを定義する可能性std::sin高くなります。stdlibc++ の定義は次のようになります。

template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, 
                                double>::__type
sin(_Tp __x)
{ return __builtin_sin(__x); }

ここから抜け出す方法はいくつかありますが、おそらくあなたはそれを好まないでしょう。上記の必要な (まだ不十分な) 変更とは別にstd::sin、目的の型に明示的にキャストする必要もあります。SameTypeここでは SFINAE 条件も削除します。これは、次の値に等しいことを強制する以外に何もしないためですType

// Declaration in the `Array` class
template <typename Return, class... Args>
inline Array<Return, Size> apply(
    Return (*f)(Type, Args&&...), 
    const Array<Args, Size>&... args
    ) const;

[...]

// Example
int main(int argc, char *argv[])
{
    Array<double, 3> x(1, 2, 3);
    //    ^^^^^^
    //    NOTICE
    //     THIS

    std::cout<<x.str()<<std::endl;

    std::cout<<x.apply((double(*)(double))std::sin).str()<<std::endl; // OK
    //                 ^^^^^^^^^^^^^^^^^^^
    //                     NOTICE THIS

    return 0;
}
于 2013-02-22T19:56:20.107 に答える