SFINAE is not applied to auto generated special member function? - c++

class Myclass{
private:
int i;
public:
Myclass(){}
Myclass(const Myclass &lvalue){} //<---
template<typename T>Myclass(T& lvalue):i(lvalue){}
};
int main()
{
Myclass a;
Myclass b(a);
return 0;
}
The code above fails to compile with:
error: cannot convert ‘Myclass’ to ‘int’ in initialization
Is this a bug? I have tested it using g++ 5.3 and clang3.9

Nope, not a bug. And this has nothing to do with SFINAE.
Let's do overload resolution on:
Myclass b(a);
We have two viable overloads:
Myclass(Myclass const&); // your copy ctor
Myclass(Myclass& ); // your ctor template, with [T=Myclass]
Both are exact matches. One of the tiebreakers in picking the best viable candidate is to select the least cv-qualified reference - which in this case is the template. This ends up trying to initialize your int with a Myclass, hence the error. (Note, there is a tiebreaker to prefer non-templates to templates - but it's a lower ranked tiebreaker than the cv-qualification on the reference).
In this case, the solution would be to introduce SFINAE to disable this constructor in the case that it should use the copy ctor. That is:
template <class T, class = std::enable_if_t<!std::is_convertible<T*, Myclass const*>::value>>
Myclass(T& );
And now this constructor won't be viable for Myclass b(a).

The other answers are good, but I thought I'd add standard references to complement. The latest draft, section Ranking implicit conversion sequences, states:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [ Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)

Related

Is copy-constructor a viable overload?

Consider the following program:
struct A {
A(int){}
A(A const &){}
};
int main() {
A y(5);
}
The variable y is direct-initialized with the expression 5.The overload resolution selects the constructor A::A(int), which is what I expect and want, but why does it happen?
It may be so for two reasons:
either the overload A::A(int) is a better match then A::A(A const &), or the second one is not a viable overload at all.
Question: In the above program, is the constructor A::A(A const &) a viable overload for initialization of y?
Yes, the rules for constructor overloading are same as for ordinary functions. The compiler is allowed to make one user-defined conversion per parameter - as pointed by Ben Voigt - in order to match the parameters with arguments. In this case it can do int->A through A(5)
This situation is the same as:
void foo(const std::string&);
void bar(const std::string&);//1
void bar(const char*);//2
//...
foo("Hello");//Is allowed
bar("Hello");//Calls 2 as it matches exactly without a need for conversion.
So, the answer, yes it's viable overload but it's not chosen because according to overloading rules the A(int) constructor is a better match.
[class.conv.ctor]/1:
A constructor that is not explicit ([dcl.fct.spec]) specifies a
conversion from the types of its parameters (if any) to the type of
its class. Such a constructor is called a converting constructor.
[ Example:
struct X {
X(int);
X(const char*, int =0);
X(int, int);
};
void f(X arg)
{
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
f({1, 2}); // f(X(1,2))
}
— end example ]
and
[over.match.copy]/1:
Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:
(1.1) The converting constructors of T are candidate functions.

C++ template: no matching function for call

I cannot understand why this program fails to compile both with g++ 7.3 or clang++ 5.0 using -std=c++14.
A can be initialized from a const int as shown. A const reference to A can also be create from a const int but call to f(const A &) with a const int fails. Why?
#include <iostream>
struct V {
int i;
template <class T>
V(const T &t) : i{t} {}
};
struct A {
int i;
A(V v) : i{v.i} {}
};
void f(const A &) {}
int main() {
const auto i = 42;
const A a1{i}; // OK
std::cout << a1.i << '\n'; // 42
const A &a2 = A{i}; // OK
std::cout << a2.i << '\n'; // 42
f(i); // no matching function for call to 'f'
return 0;
}
Given f(i);, copy initialization is applied. And i (with type const int) needs to be converted to A, two user-defined conversions are required; from const int to V, and from V to A. But only one user-defined conversion is allowed in one implicit conversion sequence.
Bot const A a1{i}; and const A &a2 = A{i}; are direct initialization, only one implicit conversion from i (with type const int) to the argument of A's constructor (i.e. V) is required, so they work fine.
Note the difference between copy initialization and direct initialization,
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
As a workaround, you can perform explicit conversion on i before passing it to f().
Converting i to an A for the purpose of the function call will require two user defined conversions (int -> V -> A). The standard places a hard limit of a single user defined conversion on each implicit conversion sequence.
The same would happen if you were to try and bind a2 to i "directly". So you need to do a functional style cast (A{i}) when giving an argument to f as well.
You need two consecutive implicit type conversion here however C++ can do single conversion implicitley for you. If you want to let the compiler to generate the correct typed code for you, use template for the function f like following.
template <typename T>
void f(const T & x) { std::cout << x << std::endl;}
The reason why you need two type conversion is because of having only one constructor which takes type V in the struct. If you want to get rid of two type conversion as a second solution, you can add another constructor which takes int as a parameter like following
struct A {
int i;
A(V v) : i{v.i} {}
A(int theI) : i{theI} { }
};
Two user defined conversions are not supported in copy initialization. Simple work around to the problem is to wrap i with A while passing to funtion f with f(A(i))

Why is the 'explicit' keyword allowing implicit conversions?

class Test {
private:
int value;
public:
void display(void)
{
cout << "Value [" << value << "]" << endl;
}
explicit Test(int i)
{
value=i;
}
};
int main() {
Test a(5);
Test b(4.9);
a.display();
b.display();
cin.get();
return 0;
}
Float value gets converted to int even though explicit is mentioned.
I was expecting (incorrectly) that float does not get converted to integer and object b not to be constructed.
explicit refers to the constructor itself, not the constructor's parameters. Your explicit constructor may not be used as an implicit conversion to type Test.
void function( Test param );
function( 5 ); // Your "explicit" makes this call an error.
// The parameter must be explicitly cast, such as Test(5)
In C++11 or later, you can prevent implicit parameter conversions using the = delete syntax on a template parameter.
Test(int i)
{
value=i;
}
template<typename T>
Test(const T&) = delete;
// ^ Aside from your int constructor and the implicitly-generated
// copy and move constructors, this will be a better match for any other type
In C++20 or later, you can prevent implicit parameter conversions using the std::same_as concept.
Test(std::same_as<int> auto i)
{
value=i;
}
explicit just prevents any implicit conversions. So if you had:
void foo(Test t);
You cannot call foo(4); because the Test constructor is explicit. You'd have to call foo(Test(4));. The explicit keyword has nothing to do with any conversions that might have to happen during construction.
From the standard [class.conv.ctor]:
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the
direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used.
Which means that Test t = 4; is also illegal, but Test t(42.0) is fine.
It's a Floating–integral conversion.
That is: it's an implicit conversion between a prvalue of type double to a prvalue of type signed int. It discards the fractional part.
TL;DR: the conversion happens between 'double' and 'int', not in your Test constructor. If you want to prevent that constructor to be called with a float or a double you can add the definition:
Test(double) = delete;
In your Test class. Live on compiler explorer

ambiguous overload with deleted move constructor

g++ 4.6.3 and 4.7.2 fail to compile the following code (in c++0x mode) if BREAK
is defined.
template<class T> struct Test {
Test(T&) {}
#ifdef BREAK
Test(T&&) = delete;
#endif
};
void func(Test<int> const&) {}
void func(Test<double> const&) {}
int main()
{
int x = 0;
func(x);
return 0;
}
The error is
error: call of overloaded 'func(int&)' is ambiguous
while clang 3.2 RC2 and VC11 (if I replace Test(T&&) = delete; with private: Test(T&&);) accept the code.
I can't see where that should be ambiguous.
Is this a g++ issue? (I don't know what to search for in the gcc bug list...)
Deleted constructors participate in overload resolution (Are the special member functions always declared?); this is necessary so that one can use deleted constructors to prevent conversions (excerpted from 8.4.3p3):
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Enforcement of function deletion comes very late in the compilation process, after overload resolution (8.4.3p2) and so overload resolution cannot distinguish between constructors on the basis of deletion. gcc is correct and clang and VC11 are incorrect.
Note that the ambiguity is in the function call expression func(x), where the argument x is an lvalue of type int and the id func denotes an overload set with parameter types in the first (only) parameter of const Test<int> & and const Test<double> &; the available conversion sequences then are:
int lvalue; int &; Test<int> temporary; const Test<int> &,
int lvalue; int rvalue; double rvalue; double &&; Test<double> temporary; const Test<double> &.
The two sequences are user-defined conversion sequences of equal rank, and so are ambiguous. The fact that the constructor Test<double>::Test(double &&) is deleted is irrelevant at this stage.
There is open bug in GCC: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54425.
CLANG is correct, GCC needs to fix this.

Why can I create cast operator to every class except self

struct B
{
};
struct A
{
operator A&() const;
operator B&() const;
};
int main()
{
const A a;
B& br = a;
A& ar = a;
}
Why can I create cast operator to B&, but not to A&.
May be it does not have much sense (one can use it to erase const modifier, as in example), but it at least inconsistent!
You can't do this because it's explicitly forbidden. N3290 § 12.3.2 states:
Such functions are called
conversion functions. No return type can be specified. If a conversion function is a member function, the
type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”. A
conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified)
same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to
it), or to (possibly cv-qualified) void.
(Emphasis mine)
This is discussed further in a note:
These conversions are considered as standard conversions for the purposes of overload resolution (13.3.3.1, 13.3.3.1.4) and
therefore initialization (8.5) and explicit casts (5.2.9).
Which explains this decision - it would interfere with the built-in mechanics too much. (For little gain).
If you really want something non-const from a const object the only smart way to do this is constructing a new instance using the copy constructor.
As a work around you could introduce a lightweight intermediary (like a smart pointer):
struct B {};
struct A {};
namespace {
B b_inst;
A a_inst;
}
struct A_wrapper {
A& inst;
// This is perfectly fine: const alters the reference, not what it refers to
operator A&() const { return inst; }
operator B&() const { return b_inst; }
A_wrapper() : inst(a_inst) {}
};
int main() {
const A_wrapper a;
B& br = a;
A& ar = a;
}
But really, wanting to do this in the first place looks like a code smell.
The proper way to do this would be to use const_cast.
For example,
#include <iostream>
using namespace std;
void f(int* p) {
cout << *p << endl;
}
int main(void) {
const int a = 10;
const int* b = &a;
// Function f() expects int*, not const int*
// f(b);
int* c = const_cast<int*>(b);
f(c);
// Lvalue is const
// *b = 20;
// Undefined behavior
// *c = 30;
int a1 = 40;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);
// Integer a1, the object referred to by c1, has
// not been declared const
*c1 = 50;
return 0;
}
Declaring a conversion to a reference to self is not ill-formed. Your problem comes at the time where your reference is initialized. As the type of the reference and the type of the initialization expression are the same, the reference is bound directly and your user defined conversion operator is never considered. Thus normal conversion rules apply and const conversion makes the code ill-formed.
Anyway, what your are doing is basically asking yourself to get shot in the foot. If you don't like constness, don't use it. If you do it consistently, it will never bother you, but it is not going to make you new friends.