5

I have the following code:

#include <experimental/string_view>

struct b_symbol {
    template <typename T>
     explicit b_symbol(T&& symbol)
        : symbol(std::forward<T>(symbol)) {
    }

    std::experimental::string_view symbol;
};

struct b_utf8 {
    template <typename T>
    explicit b_utf8(T&& value)
        : value(std::forward<T>(value)) {
    }

    std::experimental::string_view value;

};

struct value {
    explicit value(b_utf8) {}
    explicit value(b_symbol) {}

};

int main() {
    value v({b_utf8("test")});
}

You can try it on godbolt.

If I compile it with clang (3.8.0):
clang++ oload.cpp -std=c++1y
everything runs fine.

If i compile it with gcc (6.1.1 20160602)
g++ oload.cpp -std=c++1y
I get:

oload.cpp: In function ‘int main()’:
oload.cpp:30:29: error: call of overloaded ‘value(<brace-enclosed initializer list>)’ is ambiguous
     value v({b_utf8("test")});
                             ^
oload.cpp:25:14: note: candidate: value::value(b_symbol)
     explicit value(b_symbol) {}
              ^~~~~
oload.cpp:24:14: note: candidate: value::value(b_utf8)
     explicit value(b_utf8) {}
              ^~~~~
oload.cpp:23:8: note: candidate: constexpr value::value(const value&)
 struct value {
        ^~~~~
oload.cpp:23:8: note: candidate: constexpr value::value(value&&)

Why that difference?
Is the behavior of gcc correct?

EDIT: As slavanap pointed out in his answer, the error can be circumvented by removing the curly braces on call-site. Non the less I would like to know why the compilers behave differently.

Timo
  • 1,724
  • 14
  • 36

2 Answers2

1

You are passing an initialiser list to a constructor that explicitly takes either a b_utf8 or a b_symbol, neither is correct.

You must define a constructor that takes an initialiser list if you don't want to use implicit casts.

I think this is in the process of being changed for C++17 and you will be allowed to do it the clang way then.

EDIT: Interestingly

struct  b_utf8 {

  b_utf8() = default;
  //b_utf8(){}
};

struct value {
  explicit value(b_utf8) {}
};

int main() {
    value v({b_utf8()});
}

compiles but

struct  b_utf8 {

  //b_utf8() = default;
  b_utf8(){}
};

struct value {
  explicit value(b_utf8) {}
};

int main() {
    value v({b_utf8()});
}

fails overload resolution. I'm not sure why, as far as I can tell overload resolution should behave the same in those two cases.

1stCLord
  • 860
  • 5
  • 14
  • 1
    The suggestion that initialiser lists can only be passed to constructors taking `initializer_list` is wrong, and both clang's and GCC's behaviours show that it's wrong. –  Aug 08 '16 at 13:38
  • I didn't say that. – 1stCLord Aug 08 '16 at 13:39
  • 1
    `=default` is more restrictive with respect to implicit declarations, see [this question](http://stackoverflow.com/questions/20828907/the-new-keyword-default-in-c11). – starturtle Aug 08 '16 at 14:27
0

In the error it says <brace-enclosed initializer list>.

    // Why not just try to call 
    value v(b_utf8("test"));
    // instead
    value v({b_utf8("test")});
    // ?
  • Yeah, I know, that fixes the error. But non the less I would like to know why the compilers behave differently. – Timo Aug 08 '16 at 13:30
  • I would like to know why you're passing it within braces anyway. It might provide an interesting anecdote about compilers' implementations of `experimental` classes or conversions from/to `initializer_list`, but is there any practical reason to do so? – underscore_d Aug 08 '16 at 13:40
  • @underscore_d In this particular case I had no reason to pass it within curly braces, it was a mistake. – Timo Aug 08 '16 at 13:44
  • Cool. Well, I'll be interested to see what the answer is! I'll avoid speculating, as my mind is having trouble processing all the conversions that may or may not be happening there. :s – underscore_d Aug 08 '16 at 13:44