56797


以下は問題のコードの簡単な例です。

[code]

    #include<iostream>
    
    template<typename T>
    constexpr auto func(T f, T g){
        return f() + g();
    }
    
    int main(){
    
        auto f = [](){ return 1; };
        auto g = [](){ return 2; };
    
        std::cout << func(f, g) << std::endl;
    
        return 0;
    }
    
[/code]

このときラムダ式はユニークな型であるので関数オブジェクト`f`と`g`は別の型です。  
なので`func(f, g)`関数呼び出しは型が一致しないので実体化エラーです。

このときに関数オブジェクトでも関数ポインタでも渡せるようにしたいです。

`func<int(*)()>(f, g)`や`func(+f,
+g)`の様な方法もあると思いますがもともと、関数オブジェクトをテンプレートで渡すこと自体が推奨されていないことなのでしょうか？  
`std::function`や関数ポインタを引数に取らせるほうがよほどいい方法でしょうか？

追記:7/22 11:34

極端な例ですが

[code]

    template<typename T>
    void f(T a){
        if constexpr(std::is_same_v<T, int>){
            std::cout << a << std::endl;
        }else{
            static_assert([](){ return false; }());
        }
    }
    
[/code]

と

[code]

    void f(int a){...}
    
[/code]

とどちらがいいのでしょうか。(上記の例は簡単な型なのでテンプレートを使わないほうが良さそうですが...)


CC BY-SA 4.0
2019-07-21T15:54:23.233
56787
2019-07-22T03:39:23.407
2019-07-22T02:35:17.363
28294
28294
question
c++
関数テンプレートの実引数推定とラムダ式


metropolisさんのコメントとほとんど同じですが、[可変引数テンプレート](https://cpprefjp.github.io/lang/cpp11/variadic_templates.html)と[畳み込み式](https://cpprefjp.github.io/lang/cpp17/folding_expressions.html)を使うのはどうでしょうか？
可変引数なので、２個と言わず何個でも並べられます。

[code]

    template<typename... Args>
    constexpr auto func(Args... args) {
        return (... + args());
    }
    
[/code]

* * *

>
> 関数の戻り値型とシグネチャが全く異なっていてもコンパイルに通ってしまうことが思わぬ間違いに繋がりそうです。std::invoke_resultなどを使いコンパイル時に弾く方法が良いのでしょうか？

間違いかどうかは状況によります。防ぎたいのであれば防ぐよう記述するまでです。  
一例として、全ての引数が最初のものと同じ型かどうかのチェックを入れてみました。

[code]

    template<typename Arg0, typename... Args>
    constexpr auto func(Arg0 arg0, Args... args) {
        static_assert((... && std::is_same_v<std::invoke_result_t<Arg0>, std::invoke_result_t<Args>>),
            "not same result type.");
        return (std::invoke(arg0) + ... + std::invoke(args));
    }
    
[/code]


CC BY-SA 4.0
2019-07-21T20:47:51.307
56788
2019-07-22T03:39:23.407
2019-07-22T03:39:23.407
4236
4236
56787
answer


> もともと、関数オブジェクトをテンプレートで渡すこと自体が推奨されていないことなのでしょうか？

いいえ。基本的には「関数オブジェクトをテンプレートで渡す」スタイルの方が推奨されます。C++標準ライブラリでもこのスタイルを取っています。

本家SOの [std::function vs
template](https://stackoverflow.com/questions/14677997/) 質問と回答もご参考にください。

> `std::function`や関数ポインタを引数に取らせるほうがよほどいい方法でしょうか？

  * `std::function`方式は、テンプレート方式に比べると呼出しオーバーヘッドが大きくなりがちです。（賢いコンパイラであれば最適化してくれるかもしれません）
  * 関数ポインタ方式は、ラムダ式を含む関数オブジェクトが「状態」を持つケースで利用できません。つまりインタフェース設計の選択肢が狭まります。

* * *

追記にある例であれば、非テンプレートな通常関数を選ぶべきと思います。（この例示の範囲内に限れば）テンプレートを使う理由がなく、必要のない複雑性を持ち込むだけだからです。

強いて "テンプレート＋`std::is_same`"
を使う理由付けをするならば、`f('X');`のように暗黙に`int`型へ変換可能な型(`char`)を渡したときの挙動が異なります。このような厳密型一致が本当に必要であれば、テンプレート関数を使う必然性が出てきますね。

<https://wandbox.org/permlink/R4Ua2a4J008FwNoC>


CC BY-SA 4.0
2019-07-22T03:05:39.113
56797
2019-07-22T03:25:08.890
2019-07-22T03:25:08.890
49
49
56787
answer