使用std :: make_tuple时如何避免构造函数的未定义执行顺序

Eri*_*und 19 c++ tuples variadic-templates c++11

如果构造函数的执行顺序很重要,我如何使用std :: make_tuple?

例如,我猜A类的构造函数和类B的构造函数的执行顺序未定义为:

std::tuple<A, B> t(std::make_tuple(A(std::cin), B(std::cin)));
Run Code Online (Sandbox Code Playgroud)

在阅读了对该问题的评论后,我得出了这个结论

将std :: tuple转换为模板参数包

这就是说

template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
  return std::make_tuple(args(stream)...);
}
Run Code Online (Sandbox Code Playgroud)

实现具有未定义的构造函数的执行顺序.

更新,提供一些上下文:

为了给我想要做的更多背景,这里是一个草图:

我想在CodeSynthesis XSD二进制解析/序列化的帮助下从stdin中读取一些序列化对象.以下是如何完成此类解析和序列化的示例:example/cxx/tree/binary/xdr/driver.cxx

xml_schema::istream<XDR> ixdr (xdr); 
std::auto_ptr<catalog> copy (new catalog (ixdr));
Run Code Online (Sandbox Code Playgroud)

我希望能够指定序列化对象具有的类的列表(例如,目录,目录,3个序列化对象的someOtherSerializableClass)并将该信息存储为typedef

template <typename... Args>
struct variadic_typedef {};

typedef variadic_typedef<catalog, catalog, someOtherSerializableClass> myTypes;
Run Code Online (Sandbox Code Playgroud)

如何建议是否可以"存储"模板参数包而不扩展它?

并在解析完成后找到一种方法来使用std :: tuple.草图:

auto serializedObjects(binaryParse<myTypes>(std::cin));
Run Code Online (Sandbox Code Playgroud)

其中serializedObjects将具有该类型

std::tuple<catalog, catalog, someOtherSerializableClass>
Run Code Online (Sandbox Code Playgroud)

Die*_*ühl 9

简单的解决方案不是首先使用std::make_tuple(...),而是std::tuple<...>直接构造一个:调用成员的构造函数的顺序是明确定义的:

template <typename>
std::istream& dummy(std::istream& in) {
    return in;
}
template <typename... T>
std::tuple<T...> parse(std::istream& in) {
    return std::tuple<T...>(dummy<T>(in)...);
}
Run Code Online (Sandbox Code Playgroud)

功能模板dummy<T>()仅用于展开某些内容.订单由以下元素的构造顺序强加std::tuple<T...>:

template <typename... T>
    template <typename... U>
    std::tuple<T...>::tuple(U...&& arg)
        : members_(std::forward<U>(arg)...) { // NOTE: pseudo code - the real code is
    }                                        //       somewhat more complex
Run Code Online (Sandbox Code Playgroud)

在下面的讨论和Xeo的评论之后,似乎有一个更好的选择是使用

template <typename... T>
std::tuple<T...> parse(std::istream& in) {
    return std::tuple<T...>{ T(in)... };
}
Run Code Online (Sandbox Code Playgroud)

大括号初始化的使用起作用,因为大括号初始化列表中参数的求值顺序是它们出现的顺序.的语义T{...}在12.6.1 [class.explicit.init]第2款中描述说,它如下列表初始化语义规则(注:这有没有关系的std :: initializer_list只与同质类型的作品).排序约束在8.5.4 [dcl.init.list]第4段中.

  • @mmocny:我应该说"list-initialization",这是统一初始化的正确术语.所以,是的,我的意思是:"使用`std :: tuple <T ...> {T(args)...}`.它改变了这种情况.正如约翰内斯在对纳瓦兹答案的评论中正确说的那样,列表-init被指定为*always*从左到右评估. (4认同)
  • 为什么依靠一些不确定的重载决策来获得正确的构造函数?只需使用初始化列表,您就可以进行从左到右的评估. (3认同)
  • @jogojapan:我刚才注意到构造函数确实要求元素可以从参数类型中隐式构造:它在20.4.2.1 [tuple.cnstr]第9段中说(尽管我不确定是否'备注:`s是规范性的. (2认同)
  • @Nawaz我也没有发现元素子对象是否以"Types.."的顺序出现.嗯.元素需要隐式转换对我来说是有意义的,因为否则完美的转发ctor将允许比`tuple(const Types&...)`构造函数更多的初始化情况,因为它是一个函数调用也只允许隐式转换.我不确定这是否总是像宣传的那样有效. (2认同)
  • @mmocny我们将区分初始化列表,初始化列表和`std :: initializer_list <T>`.请参阅http://meta.stackexchange.com/questions/75798/unfortunate-auto-detection-of-synonyms-for-initializer-list(太容易让他们混淆!). (2认同)

归档时间:

查看次数:

1466 次

最近记录:

13 年,1 月 前