C++ で関数引数の評価順序を保証したかった

[関数引数の評価順序]

C++では関数を呼び出す際に渡される引数の評価順序は未規定になっています。
どういうことかというと次のようなコードの場合、処理系によって出力される値が異なる可能性があります。

template<typename T>
auto
print(T value){
    std::cout << value;
    return value;
}

template<typename... Args>
void
func(Args&&...){}

// 123 と出力されるかもしれないし 321 と出力されるかもしれない
func(print(1), print(2), print(3));

上記のようなコードの場合、 clang では 123 と出力されますが、gcc では 321 と出力されます。 ただし、これは関数呼び出しの場合のみで、コンストラクタの {} 呼び出しや、初期化リストの {} では順序が『左から右の順で評価される』ことが保証されています。 ですので次のような初期化リストを呼び出した場合は期待通りの動作を行います。

template<typename T>
auto
print(T value){
    std::cout << value;
    return value;
}

// 123 と出力されることが保証されている
auto dummy = { print(1), print(2), print(3) };

[可変長テンプレート引数で引数分関数を呼び出したい]

上記の仕様を踏まえた上で、次のような処理を考えてみます。

template<typename F, typename... Args>
auto
call(F f, Args... args){
    // 可変長引数の数だけ f を呼び出してその結果を tuple で返す
    return std::make_tuple(f(args)...);
}

using namespace std::literals::string_literals;
auto twice = [](auto a){ return a + a; };
auto result = call(twice, 1, 3.5, "homu"s);

assert(result == std::make_tuple(2, 7.0, "homuhomu"));

『可変長引数の数だけ f を呼び出してその結果を tuple として返す』という簡単な関数です。 しかし、残念なことに std::make_tuple(f(args)...) という関数呼び出しでは、評価順序が処理系によって異なってしまいます。

auto print = [](auto a){
    std::cout << a << std::endl;
    return a;
};
// print の呼び出しが不規定なのでどういう出力になるのかが保証されてない
call(print, 1, 3.5, "homu"s);

[std::make_tuple ではなくて std::tuple コンストラクタを使う]

上記の問題は std::make_tuple ではなくて std::tuple のコンストラクタを呼び出すことで回避できます。

template<typename F, typename... Args>
auto
call(F f, Args... args){
    // std::tuple のコンストラクタを呼び出すことで評価順序を保証する
    return std::tuple<decltype(f(args))...>{f(args)...};
}

auto print = [](auto a){
    std::cout << a << std::endl;
    return a;
};
// 出力結果が保証される
call(print, 1, 3.5, "homu"s);

これで意図した順番で評価される事が保証されます。

[参照]