Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of evaluation of elements in list-initialization

In the other topic, @Dietmar gave this solution:

template <typename... T>
std::tuple<T...> parse(std::istream& in) 
{
    return std::tuple<T...>{ T(in)... };
}

stating that,

The use of brace initialization works because the order of evaluation of the arguments in a brace initializer list is the order in which they appear. (emphasize mine)

The relevant text from the C++ Standard (n3485) is,

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. —end note ]


So I tried to test this with the following code:

template<int N>
struct A 
{ 
    std::string data;
    A(std::istream & stream) { stream >> data; }
    friend std::ostream& operator<<(std::ostream & out, A<N> const & a) 
    {
        return out << "A"<<N<<"::data = " << a.data;
    }
};
typedef A<1> A1;
typedef A<2> A2;

template<typename ...Args>
void test(std::istream & stream)
{
    std::tuple<Args...> args { Args(stream)... };
    std::cout << std::get<0>(args) << std::endl;
    std::cout << std::get<1>(args) << std::endl;
}

int main()
{
    std::stringstream ss("A1 A2");
    test<A1,A2>(ss);
}

Expected output:

A1::data = A1
A2::data = A2

Actual Output:

A1::data = A2
A2::data = A1

Did I do anything wrong in my test code? I changed my code to this:

std::stringstream ss("A1 A2");
std::tuple<A1,A2> args{A1(ss), A2(ss)};
std::cout << std::get<0>(args) << std::endl;
std::cout << std::get<1>(args) << std::endl

Same output as before. I tested my code with MinGW (GCC) 4.7.0 and 4.7.2. Even ideone gives this output.

Is it a bug in the compiler?

like image 594
Nawaz Avatar asked Dec 27 '12 19:12

Nawaz


People also ask

Is initialization list faster?

Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment.

What is initialization list in C?

The initializer list is used to directly initialize data members of a class. An initializer list starts after the constructor name and its parameters.


1 Answers

Answering my own question. Deleting the question would not be a good idea, as someone might have the same question in the future.

Yes. It is a bug in the GCC compiler.

  • Bug 51253 - [C++11][DR 1030] Evaluation order (sequenced-before relation) among initializer-clauses in braced-init-list

taken from @Johannes Schaub's comment to the question.

like image 164
Nawaz Avatar answered Sep 30 '22 08:09

Nawaz