C++ Constructor Overload Resolution with Multiple Inheritance - c++

I've got this short snippet of code that I'd like to get a bit more information about as to why overload resolution is picking one constructor over another. Here's the code in question:
#include <iostream>
struct Base
{
};
struct Other
{
Other(const Other&)
{
std::cout << "Copy Constructor\n";
}
Other(const Base&)
{
std::cout << "Custom Constructor\n";
}
};
struct Derived : public Base, public Other
{
Derived() :
Other(*this)
{
}
};
int main()
{
Derived derived; // Prints "Copy Constructor"
system("pause");
return 0;
}
I'm assuming there's a section in the C++ Standard that defines the copy constructor as a better match than user defined constructors*? My assumption otherwise was that if no rule favoring the copy constructor existed, then the compiler would either go by the order of inheritance (as with the order of construction with multiple inheritance) or just give me an ambiguous constructor call error. However, reversing the order in which Derived inherits from Base and Other doesn't change the output, which leads me to believe that my initial guess about copy constructors being favored is correct. Could anyone point me to the rule that determines the behavior I'm seeing?
* I checked cppreference.com's Overload Resolution page, but I didn't see any rule listed there that would explain the behavior I'm seeing (though I'm
not completely fluent in Standardese, so I could have easily missed it).

The reason that the code snippet in question compiles is due to non-standard conforming behavior from Visual Studio (I'm currently using VS2017.3 Preview, which compiles the code without errors even with the /permissive- flag). Below is the error emitted by GCC and Clang:
GCC
Error(s):
source_file.cpp: In constructor ‘Derived::Derived()’:
source_file.cpp:25:20: error: call of overloaded ‘Other(Derived&)’ is ambiguous
Other(*this)
^
source_file.cpp:16:5: note: candidate: Other::Other(const Base&)
Other(const Base&)
^
source_file.cpp:12:5: note: candidate: Other::Other(const Other&)
Other(const Other&)
^
Clang
Error(s):
source_file.cpp:25:9: error: call to constructor of 'Other' is ambiguous
Other(*this)
^ ~~~~~
source_file.cpp:12:5: note: candidate constructor
Other(const Other&)
^
source_file.cpp:16:5: note: candidate constructor
Other(const Base&)
^
1 error generated.
Error output taken from http://rextester.com/.

Related

Defaulted concept-constrained function never selected for instantiation

While working with C++20, I encountered an oddity which I'm unsure is a compiler defect due to C++20's infancy, or whether this is correct behavior.
The problem is that a specific constrained function is either not selected correctly, or produces a compile-error -- depending entirely on the order of the definitions.
This occurs in specific circumstance:
A constructor / destructor / member-function is constrained by requires, and
This constructor / destructor / member-function is implicitly deleted for some instantiation of T that does not satisfy the requires clause
For example, consider this basic naive_optional<T> implementation:
template <typename T>
class naive_optional {
public:
naive_optional() : m_empty{}, m_has_value{false}{}
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
~naive_optional() {
if (m_has_value) {
m_value.~T();
}
}
private:
struct empty {};
union {
T m_value;
empty m_empty;
};
bool m_has_value;
};
This code works fine for trivial types like naive_optional<int>, but fails to instantiate for non-trivial types such as naive_optional<std::string> due to an implicitly deleted destructor:
<source>:26:14: error: attempt to use a deleted function
auto v = naive_optional<std::string>{};
^
<source>:8:5: note: explicitly defaulted function was implicitly deleted here
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
^
<source>:17:11: note: destructor of 'naive_optional<std::basic_string<char>>' is implicitly deleted because variant field 'm_value' has a non-trivial destructor
T m_value;
^
1 error generated.
Live Example
If the order of definitions is changed so that the constrained function is below the unconstrained one, this works fine -- but instead selects the unconstrained function every time:
...
~naive_optional() { ... }
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
...
Live Example
My question is ultimately: is this behavior correct -- am I misusing requires? If this is correct behavior, is there any simple way to accomplish conditionally default-ing a constructor / destructor / function that is not, itself, a template? I was looking to avoid the "traditional" approach of inheriting base-class implementations.
Your code is correct, and indeed was the motivating example for supporting this sort of thing (see P0848, of which I am one of the authors). Clang simply doesn't implement this feature yet (as you can see here).
gcc and msvc both accept your code.
Note that you don't need empty anymore, just union { T val; } is sufficient in C++20, thanks to P1331. Demo.

Compile error when using std::variant with gmock 1.8 object

Let's have this code using gmock 1.8:
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <variant>
class Obj {
public:
MOCK_METHOD0( mock, void() );//<-!!!
};
using Variant = std::variant<Obj>;
TEST(Test, Test) {
Obj obj;
Variant variant = obj;
}
When trying to compile this with clang++ there is this compile error:
error: no viable conversion from 'Obj' to 'Variant' (aka 'variant<Obj>')
Variant variant = obj;
^ ~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/variant:1081:7: note: candidate constructor not viable: no known conversion from 'Obj' to 'const std::variant<Obj> &' for 1st argument
variant(const variant& __rhs) = default;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/variant:1093:2: note: candidate template ignored: substitution failure [with _Tp = Obj &, $1 = void, $2 = void]: implicit instantiation of undefined template 'std::variant<Obj>::__to_type_impl<18446744073709551615, false>'
variant(_Tp&& __t)
^
(g++'s error only says
error: conversion from ‘Obj’ to non-scalar type ‘Variant’ {aka ‘std::variant<Obj>’} requested
Variant variant = obj;
^~~
)
When the line with MOCK_METHOD0 macro is commented out, the code compiles all right. I'm convinced it's the copy constructor (the one from line 1081) that gets invoked in that case.
Why is that? Will the problem disappear if I move to gmock 1.10? Or is it impossible to use variants of gmock mocks? Thanks for any explanation or a hint on how to find it myself.
If you expand the MOCK_METHOD0 macro, you will see that it inserts the following data member to your class definition:
class Obj
{
public:
// ...
mutable ::testing::FunctionMocker<void()> gmock0_mock_12;
};
Since you do not provide your own copy-constructor, one will be implicitly generated by the compiler. Its default implementation will try to perform a member-wise copy. However, checking the definition of FunctionMocker, one can see the following notice:
// There is no generally useful and implementable semantics of
// copying a mock object, so copying a mock is usually a user error.
// Thus we disallow copying function mockers. If the user really
// wants to copy a mock object, they should implement their own copy
// operation, for example:
//
// class MockFoo : public Foo {
// public:
// // Defines a copy constructor explicitly.
// MockFoo(const MockFoo& src) {}
// ...
// };
FunctionMocker(const FunctionMocker&) = delete;
FunctionMocker& operator=(const FunctionMocker&) = delete;
That is, FunctionMocker objects are non-copyable, and so are mock objects (their implicitly generated copy-constructors are also deleted).
That's reasonable. Should a copy of a mock have the same expectations? Should it double-check the expectations on destruction?
If you want to put the mock in a variant, you can build it in-place, either:
std::variant<Obj> variant(std::in_place_type_t<Obj>{});
or:
std::variant<Obj> variant;
variant.emplace<Obj>();

Correct way to initialize custom struct object in a class constructor

In the constructor for myClass, in the code below, I initialize ms with ms({4,5}).
#include <iostream>
class myClass {
private:
struct myStruct {
int val1;
int val2;
};
public:
myStruct ms;
myClass() : ms({4,5}){} // This is where I initialize ms!
};
int main(){
myClass mc;
std::cout << mc.ms.val1 << " " << mc.ms.val2 << std::endl;
return 0;
};
However, we are in the member initialization list, so the arguments to ms are the arguments to the ms's constructor. What happens here,
essentially, is that we use the so-called "list initialization" to
create an unnamed (anonymous) myStruct object: "{4,5}", and then we
pass that object to the constructor of ms. So in principle, the
anonymous intermediate object is created, copied to initialize ms,
and then destroyed (although I'm pretty sure that the compiler knows
better and initializes ms directly; but there is still the
semantic difference). I was told that, instead of ms({4,5}) I could simply do
ms(4,5)
and avoid making an intermediate object. However, when I do this, I get the following output:
g++ -g -std=c++11 -Wall temp.cpp -o temp
temp.cpp: In constructor ‘myClass::myClass()’:
temp.cpp:12:21: error: no matching function for call to ‘myClass::myStruct::myStruct(int, int)’
myClass() : ms(4,5){}
^
temp.cpp:5:10: note: candidate: myClass::myStruct::myStruct()
struct myStruct {
^
temp.cpp:5:10: note: candidate expects 0 arguments, 2 provided
temp.cpp:5:10: note: candidate: constexpr myClass::myStruct::myStruct(const myClass::myStruct&)
temp.cpp:5:10: note: candidate expects 1 argument, 2 provided
temp.cpp:5:10: note: candidate: constexpr myClass::myStruct::myStruct(myClass::myStruct&&)
temp.cpp:5:10: note: candidate expects 1 argument, 2 provided
makefile:15: recipe for target 'temp' failed
make: *** [temp] Error 1
So, to be clear, I am wondering what the best practice is, in terms of initialising a struct object in a class constructor. All help is much appreciated!
I was told that, instead of ms({4,5}) I could simply do ms(4,5)
That's almost right, but not quite. What you can actually do is ms{4,5}, that is, use direct-list-initialisation for the member ms. That way, it will use normal list initialisation rules, which in your case will invoke aggregate initialisation.
In other words, the member ms will be initialised directly in-place, without creating any temporary objects of type myStruct.

Deleted default constructor identified as candidate in ambiguous error

When attempting to delete the default constructor and provide a new one with a default parameter (and therefore still be able to default construct the object), I receive an ambiguity error from g++.
class Thing
{
public:
Thing() = delete;
Thing(int arg = 0) : arg(arg) {}
private:
int arg;
};
int main(int, char**)
{
Thing thing;
return 0;
}
Compilation error below:
$ g++ deletedConstructorTest.C -std=c++11
deletedConstructorTest.C: In function "int main(int, char**)":
deletedConstructorTest.C:12:9: error: call of overloaded "Thing()" is ambiguous
Thing thing;
^
deletedConstructorTest.C:12:9: note: candidates are:
deletedConstructorTest.C:5:5: note: Thing::Thing(int)
Thing(int arg = 0) :arg(arg) {}
^
deletedConstructorTest.C:4:5: note: Thing::Thing() <deleted>
Thing() = delete;
^
If I modify the example above by removing the "Thing() = delete;" line then it compiles fine. I understand that the compiler won't generate a default constructor since I provided my own so that line is unnecessary but I was surprised at the compilation error.
Thanks in advance!
All declared overloads are considered during overload resolution, even if they are deleted or inaccessible. So you've declared two default constructors, both of which are an equally good match for default-initialisation, hence the ambiguity.
In this case, simply don't declare the zero-argument default constructor at all. The one-argument constructor is usable as a default constructor, and declaring any constructor inhibits the generation of an implicit default constructor.

Does Hinnant's unique_ptr implementation incorrectly fail to convert derived-to-base in this case?

I'm currently trying to use Howard Hinnant's unique_ptr implementation, and am running into a compile error. Here is some sample code:
struct Base {};
struct Derived : public Base {};
void testfun(boost::unique_ptr<Base>);
void test()
{
unique_ptr<Derived> testDerived;
unique_ptr<Base> testBase(move(testDerived)); // ok, construct base explicitly from derived
testfun(move(testBase)); // ok, pass base to testfun which expects base
testfun(unique_ptr<Base>(move(testDerived))); // ok, explicitly converts to unique_ptr<Base>
testfun(move(testDerived)); // error on this line
}
The error I get is
In function 'void test()':
error: no matching function for call to 'boost::unique_ptr<Base, boost::default_delete<Base> >::unique_ptr(boost::unique_ptr<Base, boost::default_delete<Base> >)'
note: candidates are: boost::unique_ptr<T, D>::unique_ptr(boost::detail_unique_ptr::rv<boost::unique_ptr<T, D> >) [with T = Base, D = boost::default_delete<Base>]
note: boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<T, D>&) [with T = Base, D = boost::default_delete<Base>]
error: initializing argument 1 of 'void testfun(boost::unique_ptr<Base, boost::default_delete<Base> >)' from result of 'boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<U, E>, typename boost::enable_if_c<((((! boost::is_array<U>::value) && boost::detail_unique_ptr::is_convertible<typename boost::unique_ptr<U, boost::default_delete<U> >::pointer,typename boost::detail_unique_ptr::pointer_type<T, D>::type>::value) && boost::detail_unique_ptr::is_convertible<E,D>::value) && ((! boost::is_reference<D>::value) || boost::is_same<D,E>::value)), void>::type*) [with U = Derived, E = boost::default_delete<Derived>, T = Base, D = boost::default_delete<Base>]'
It seems like the offending line should not fail. Is this a bug in the implementation, a limitation of the implementation due to the lack of C++0x language features, or a misunderstanding of the rules of unique_ptrs?
(Note, I know this won't work at run-time because I'm moving the same thing more than once; I'm just trying to figure out the compile-time error.)
For a similar example, see this which should fail too
unique_ptr<Base> testBase = move(testDerived);
The problem here is how the move semantic is implemented: The "copy constructor" takes a non-const reference, thus not being able to bind to temporaries. To still "move" from temporaries, the class has a conversion function (following are really just conceptual - they may be differently implemented in detail):
operator rv<T>() { return rv<T>(*this); }
And a constructor will take that object:
unique_ptr(rv<T> r):ptr_(r.release()) { }
Here is an example that fails for the same reason:
// move helper. rv<> in unique_ptr
struct E { };
// simulates a unique_ptr<D>
struct D { };
// simulates the unique_ptr<B>
struct A {
A() { }
// accepts "derived" classes. Note that for unique_ptr, this will need that
// the argument needs to be copied (we have a by-value parameter). Thus we
// automatically ensure only rvalue derived-class pointers are accepted.
A(D) { }
// these will accept rvalues
A(E) { }
operator E() { return E(); }
private:
A(A&); // private, error if passed lvalue
};
Now, consider this code:
// allowed: goes: D -> A(D)
A a((D()));
// compile failure. Goes:
// D -> A(D) -> A(E)
A a = D();
Copy initialization will first convert to A. But then, the temporary A object is tried to be copied again to the final object. This will need the way using operator E. But that's another user defined conversion in the initialization, which the standard forbids:
13.3.3.1/4
When invoked for the copying of the temporary in the second step of a class copy-initialization, [...], only standard conversion sequences and ellipsis conversion sequences are allowed.
This is why your code fails.
Further research has lead me to this note, leading me to believe that this is a known limitation of the implementation:
3 of the tests currently fail for me (fail at compile time,
supposed to compile, run and pass). These are all associated with the
converting constructor specified in [unique.ptr.single.ctor]. When
the source and target are of different type, this emulation demands
that the conversion be explicit, and refuses to compile on implicit
conversions:
unique_ptr<base> b(unique_ptr<derived>()); // ok
unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor .