22

Consider the following program:

#include <iostream>

class A
{
public:
  A( ) { std::cout << "A()\n"; }

  A( A& ) = delete;

  A( int i ) { std::cout << "A( " << i << " )\n"; }

  explicit operator int( ) { std::cout << "operator int()\n"; return 42; }
};

template< typename T = A > void f( T a = A() ) {}

int main( void )
{
  f();
  return 0;
}

Visual Studio 2013 compiles this code and runs, with output

A()
operator int()
A( 42 )

Is this a compiler bug? It looks like the VS compiler doesn't heed the 'explicit' keyword in this context. From my understanding, VS 2013 wrongly uses operator int() in combination with A(int) to sort-of 'copy-construct' A as the default parameter for f.

Both adding

A a;
A a1( a );

to main and declaring f as

void f( A a = A() ) {}

does not compile, VS complains that A(A&) is deleted, which seems to be correct behavior. Only in the context of the function template default parameter the combination of operator int() and A(int) seem to work as a substitution for A( A& ).

g++ 4.7.3 does not compile the code and complains:

main.cpp: In function ‘int main()’:
main.cpp:21:7: error: no matching function for call to ‘A::A(A)’
main.cpp:21:7: note: candidates are:
main.cpp:10:3: note: A::A(int)
main.cpp:10:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’
main.cpp:6:3: note: A::A()
main.cpp:6:3: note:   candidate expects 0 arguments, 1 provided

Removing 'explicit' makes g++ compile the code and the output is the same.

KAI42
  • 243
  • 1
  • 7
  • 6
    Applying `explicit` to conversion operators (as opposed to constructors) is new in C++11. Maybe the version of VC++ you're using simply hasn't implemented that feature yet (or maybe the flags you're using don't enable it)? – Jeremy Roman Dec 10 '13 at 15:19
  • 1
    msvc allows non-const references to temporaries, I wouldnt be surprised if they chose to do things that way because they like it more. – PlasmaHH Dec 10 '13 at 15:20
  • @JeremyRoman the feature was newly introduced in VS 2013 and it correctly works in the two last examples, so I guess the support for explicit was just overlooked in this context? – KAI42 Dec 10 '13 at 15:25
  • @Mgetz if I either make A( A& ) = delete; or A( A& ) {} private the code still compiles and the output is still the same. – KAI42 Dec 10 '13 at 15:49
  • @KAI42 I'm [not seeing any reason](http://gcc.gnu.org/gcc-4.7/cxx0x_status.html) why that version of GCC should be doing this, have you checked the bugs that were fixed for the 4.8 release? – Mgetz Dec 10 '13 at 15:58
  • 1
    @Mgetz: `21 : error : no matching function for call to ‘A::A( A )’ f( 42 ); ^ 21 : note : candidates are : 10 : note : A::A( int ) A( int i ) { std::cout << "A( " << i << " )\n"; } ^ 10 : note : no known conversion for argument 1 from ‘A’ to ‘int’ 6 : note : A::A( ) A( ) { std::cout << "A()\n"; } ^ 6 : note : candidate expects 0 arguments, 1 provided` sorry it doesn't look like I can format code here ;) 4.8 still complains. clang does too, and icc too. I used http://gcc.godbolt.org/ to try this. – KAI42 Dec 10 '13 at 16:05
  • @KAI42 have you tried to define an assignment operator? by explicitly deleting the copy constructor it would seem to be a violation of [The Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). Also onto your complaint about VC 2013 does not support SFINAE yet so that is expected. – Mgetz Dec 10 '13 at 16:10
  • @Mgetz I tried adding a private operator=, still the same behavior. Do you really think this is a SFINAE issue? – KAI42 Dec 10 '13 at 16:20
  • @KAI42 As far as "VS complains that `A(A&)` is deleted" yes in some sense; VS does not support recognizing that the substitution failure isn't an error in this case. However for that assignment to work you either need a public assignment or move operator or a public copy or move constructor. – Mgetz Dec 10 '13 at 16:23
  • I don't want to get the assignment to work. I want to know why Visual Studio doesn't see the fact that the operator int() is explicit and uses it in an assignment where it shouldn't use it =) Concerning SFINAE: That VS complains that A(A&) is deleted is correct behavior. That it doesn't complain in the first example seems wrong to me. And several other compilers seem to be of the same opinion. – KAI42 Dec 10 '13 at 17:57
  • I changed the question text a little, seems the issue isn't easily explained. – KAI42 Dec 10 '13 at 18:11
  • If you do `A(const A&) = delete;`, then GCC will fail immediately telling you that `T a = A()` is using a deleted function. It won't even consider the path that leads through the conversion operator. Is this expected? I don't immediately understand the GCC's logic. – AnT stands with Russia Dec 10 '13 at 18:24
  • @AndreyT `= delete` doesn't mean "remove this signature from the overload set" it means "forbid calls to this signature." – Casey Dec 10 '13 at 18:34

2 Answers2

7

This is definitely a bug in Visual C++. According to standard:

12.3.2 Conversion functions [class.conv.fct]

2 - A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5) in certain contexts (13.3.1.4, 13.3.1.5, 13.3.1.6).

and there is no direct-initialization in your example.

Other C++ compilers such as GCC and Clang report an error in this case.

vitaut
  • 49,672
  • 25
  • 199
  • 336