调用函数对象时如何保证参数评估的顺序?

Die*_*ühl 18 c++ c++11

关于如何在使用std :: make_tuple时避免构造函数的未定义执行顺序的问题的答案导致了一个讨论,在此期间我了解到构造函数可以保证参数评估的顺序:使用braced-init-list命令保证从左到右:

T{ a, b, c }
Run Code Online (Sandbox Code Playgroud)

表达式a,bc,按给定的顺序进行评估.即使类型T只定义了普通的构造函数,也是如此.

显然,并非所有被调用的都是构造函数,有时候在调用函数时保证求值顺序会很好,但是没有像brace-argument-list这样的东西来调用函数,并且定义了对它们的参数的评估顺序.问题变成:构造函数的保证是否可以用于构建函数调用工具(" function_apply()"),并具有用于评估参数的排序保证?要求调用函数对象是可以接受的.

Ker*_* SB 14

那个像这样的愚蠢的包装类怎么样:

struct OrderedCall
{
    template <typename F, typename ...Args>
    OrderedCall(F && f, Args &&... args)
    {
        std::forward<F>(f)(std::forward<Args>(args)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

用法:

void foo(int, char, bool);

OrderedCall{foo, 5, 'x', false};
Run Code Online (Sandbox Code Playgroud)

如果你想要一个返回值,你可以通过引用传递它(你需要一些特性来提取返回类型),或者将它存储在对象中,以获得如下界面:

auto x = OrderedCall{foo, 5, 'x', false}.get_result();
Run Code Online (Sandbox Code Playgroud)

  • @JiveDadson:A*braced-init-list*确保按照12.6.1 [class.explicit.init]第2段和第8.5.4节[dcl.init.list]第4段保证评估顺序....和我怀疑旧编译器是否符合C++ 2011. (4认同)
  • 有趣的是,使用`OrderedCall {&FOO,科幻(),FD(),FB()};`上的MacOS调用'FB()`,`然后FD()`,并且最后`音响()`使用gcc.对于clang,顺序是,如预期的那样,`fi()`,`fd()`和`fb()`. (2认同)

Die*_*ühl 5

我提出的解决方案std::tuple<...>用于将参数放在一起,而不是使用此对象的元素调用函数对象.优点是它可以推断出返回类型.实际的具体逻辑如下所示:

template <typename F, typename T, int... I>
auto function_apply(F&& f, T&& t, indices<I...> const*)
    -> decltype(f(std::get<I>(t)...)) {
    f(std::get<I>(t)...);
}

template <typename F, typename T>
auto function_apply(F&& f, T&& t)
    -> decltype(function_apply(std::forward<F>(f), std::forward<T>(t),
                               make_indices<T>())) {
    function_apply(std::forward<F>(f), std::forward<T>(t),
                   make_indices<T>());
}
Run Code Online (Sandbox Code Playgroud)

...使用这样的表达式调用:

void f(int i, double d, bool b) {
    std::cout << "i=" << i << " d=" << d << " b=" << b << '\n';
}

int fi() { std::cout << "int\n"; return 1; }
double fd() { std::cout << "double\n"; return 2.1; }
bool fb() { std::cout << "bool\n"; return true; }

int main()
{
    std::cout << std::boolalpha;
    function_apply(&f, std::tuple<int, double, bool>{ fi(), fd(), fb() });
}
Run Code Online (Sandbox Code Playgroud)

主要缺点是这种方法需要规范std::tuple<...>元素.另一个问题是MacOS上当前版本的gcc以与它们出现的相反顺序调用函数,即,不遵循braced-init-list中的评估顺序(gcc bug)或不存在(即,我误解了使用braced-init-list的保证.同一平台上的clang以预期的顺序执行函数.

used函数make_indices()只是创建一个指向类型对象的合适指针,indices<I...>其中索引列表可用于std::tuple<...>:

template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
    typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
    typedef typename indices<Index - 1, Index, Indices...>::type type;
};

template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)