14

The question is simple. Is it possible to construct such a type T, for which the following two variables declarations will produce different results?

T t1 = {};
T t2{};

I've been digging through the cppreference and the standard for more than an hour now, and I understood the following:

But the last one is tricky since the "effects of list initialization" is an impressive... list. Which for classes, fundamental types and aggregates seems to boil down to value initialization. But I am not sure I haven't missed anything.

Maybe you can provide a context, in which the two declarations will have different effects?

UPD: Excellent answers about explicit constructors! Next level: is it possible that both statements compile, but have different effects on compile/run time?

Mikhail
  • 20,685
  • 7
  • 70
  • 146
  • Re: UPD. Now you're just being difficult! I have found a case where both *fail* to compile (but for different reasons), but working on one where they both compile but give different results. Tricksy. – Adrian Mole May 25 '20 at 18:43
  • I can do it by changing your first case to this: `T t1 = std::initializer_list{};`. Would you consider that, or am I cheating? – Adrian Mole May 25 '20 at 19:12
  • `T t1 = std::initializer_list{};` is definitely a cheating, since I am interested in the exact forms I've provided. – Mikhail May 25 '20 at 19:22
  • "I have found a case where both fail to compile (but for different reasons)" - That's also interesting! Can you please share it? – Mikhail May 25 '20 at 19:23
  • On a related note, turns out assignment `T t; t = {};` can behave differently from `t = T{};` if its `operator=` has an `initializer_list` overload. E.g. if you assign `{}` to a container of non-copyable (possibly movable) elements, you get an error (compiler wants to have a copy ctor to copy 0 elements from the init list), while `= T{}` does work. – HolyBlackCat May 25 '20 at 19:23
  • @Mikhail Actually, I was wrong - I *can* make both fail, but they fail with the *same* error message. Sorry. – Adrian Mole May 25 '20 at 19:30
  • @HolyBlackCat Yeah, `T t1; t1 = {};` calls an assignment function, but the original `T t1 = {}` is ***not*** an assignment, so any defined `=` operator isn't called, or even considered. Tried that one. – Adrian Mole May 25 '20 at 19:33
  • @AdrianMole I kind of expected `T t1 = {}` to prefer an `initializer_list` contructor, but nope. – HolyBlackCat May 25 '20 at 19:34
  • 1
    It's a good 'un, to be sure! As soon as you put a value inside the braces then it become readily doable. The problem is in getting the compiler to see the `{}` explicitly as an (empty) initializer list. – Adrian Mole May 25 '20 at 19:36
  • Does this answer your question? [Why does the standard differentiate between direct-list-initialization and copy-list-initialization?](https://stackoverflow.com/questions/13461027/why-does-the-standard-differentiate-between-direct-list-initialization-and-copy) – Language Lawyer May 25 '20 at 20:37
  • Note, I did a [twitter quiz on a similar case recently w/ bool and nullptr](https://twitter.com/shafikyaghmour/status/1259674908553928708) – Shafik Yaghmour May 31 '20 at 18:48

2 Answers2

7

If you consider a case in which one statement will compile, but the other will not compile as "different effects," then yes, here's a context:

#include <iostream>

class T {
public:
    int data{ 0 };
    explicit T() {
        data = 0;
        std::cout << "Default constructor" << std::endl;
    }
};

int main()
{
    T t1 = {};
    T t2{};
    return 0;
}

The line declaring/initializing t1 gives the following, with clang-cl:

error : chosen constructor is explicit in copy-initialization

The MSVC compiler also complains:

error C2512: 'T': no appropriate default constructor available
message : Constructor for class 'T' is declared 'explicit'

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
4

The difference is in explicit. I've managed to make msvc difference, but it looks like a compiler bug:

#include <iostream>
#include <initializer_list>

struct T
{
    template<class... A>
    T(A...) {std::cout << "1\n";}
    explicit T() { std::cout << "2\n"; }

};


int main()
{
    T t1 = {}; // 1
    T t2{}; // 2
}
Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79