Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do gcc and clang each produce different output for this program? (conversion operator vs constructor)

program:

#include <stdio.h>  struct bar_t {     int value;     template<typename T>     bar_t (const T& t) : value { t } {}      // edit: You can uncomment these if your compiler supports     //       guaranteed copy elision (c++17). Either way, it      //       doesn't affect the output.      // bar_t () = delete;     // bar_t (bar_t&&) = delete;     // bar_t (const bar_t&) = delete;     // bar_t& operator = (bar_t&&) = delete;     // bar_t& operator = (const bar_t&) = delete; };  struct foo_t {     operator int   () const { return 1; }     operator bar_t () const { return 2; } };  int main () {     foo_t foo {};     bar_t a { foo };     bar_t b = static_cast<bar_t>(foo);      printf("%d,%d\n", a.value, b.value); } 

output for gcc 7/8:

2,2 

output for clang 4/5 (also for gcc 6.3)

1,1 

It seems that the following is happening when creating the instances of bar_t:

For gcc, it calls foo_t::operator bar_t then constructs bar_t with T = int.

For clang, it constructs bar_t with T = foo_t then calls foo_t::operator int

Which compiler is correct here? (or maybe they are both correct if this is some form of undefined behaviour)

like image 742
verb_noun Avatar asked May 18 '17 22:05

verb_noun


People also ask

Are clang and GCC interchangeable?

TL;DR: Clang is highly compatible to GCC - just give it a go. In most cases, Clang could be used as a GCC drop in replacement ( clang and clang++ are "GCC compatible drivers"). Clang was designed with GCC compatiblity in mind, and most options available in GCC should also be supported by Clang.

Does clang use GCC?

Clang is compatible with GCC. Its command-line interface shares many of GCC's flags and options. Clang implements many GNU language extensions and compiler intrinsics, some of which are purely for compatibility.


1 Answers

I believe clang's result is correct.

In both bar_t a { foo } direct-list-initialization and in a static_cast between user defined types, constructors of the destination type are considered before user defined conversion operators on the source type (C++14 [dcl.init.list]/3 [expr.static.cast]/4). If overload resolution finds a suitable constructor then it's used.

When doing overload resolution bar_t::bar_t<foo_t>(const foo_t&) is viable and will be a better match than one to any instantiation of this template resulting in the use of the cast operators on foo. It will also be better than any default declared constructors since they take something other than foo_t, so bar_t::bar_t<foo_t> is used.


The code as currently written depends on C++17 guaranteed copy elision; If you compile without C++17's guaranteed copy elision (e.g. -std=c++14) then clang does reject this code due to the copy-initialization in bar_t b = static_cast<bar_t>(foo);.

like image 54
bames53 Avatar answered Oct 16 '22 01:10

bames53