templates copy constructor errors - c++

Here is a minimal code that shows the problem:
template<typename To, typename From> To convert(const From& x);
struct A
{
int value;
template<typename T> A(const T& x) { value = convert<A>(x).value; }
};
struct B : public A { };
int main()
{
B b;
A a = b;
}
It gives me: undefined reference to 'A convert<A, B>(B const&)'
As expected, as I removed the default copy constructor. But if I add this line to A:
A(const A& x) { value = x.value; }
I get the same error. If I try to do this way: (adding a template specialization)
template<> A(const A& x) { value = x.value; }
I get: error: explicit specialization in non-namespace scope 'struct A'.
How to solve it?
My compiler is MinGW (GCC) 4.6.1
EDIT:
The convert functions converts from many types to A and back again. The problem is that don't make sense writing a convert function from B to A because of the inheritance. If I remove the line that calls convert from A it just works. The idea is to call convert for all times that do't inherit from A, for these, the default constructor should be enough.

As for as I understand, when b is passed, as b is not an object of A, copy constructor is not called, instead the template constructor is called.
However, if an object of the derived class is passed, you want the copy constructor of A to be called.
For this, there is one solution using <type_traits> (c++0x):
#include <type_traits>
template<typename To, typename From> To convert(const From& x);
struct A
{
int value;
template<typename T> A(const T& x,
const typename std::enable_if<!std::is_base_of<A,T>::value, bool>::type = false)
{ value = convert<A>(x).value; }
A(){}
};
struct B : public A { };
int main()
{
B b;
A a = b;
}
The template is disabled why an object of a class derived from A is passed, so the only available constructor is the copy constructor.

You can solve it by defining the convert function :
template<typename To, typename From> const To& convert(const From& x)
{
return x;
}

As expected, as I removed the default copy constructor.
No; while you do need to replace the default copy constructor, its omission causes a different sort of problem (and only if you have the sort of calling code that needs it).
The error you report:
undefined reference to 'A convert<A, B>(B const&)
is a linker error. It is telling you that you don't actually have a convert function anywhere.
error: explicit specialization in non-namespace scope 'struct A'
You were right the first time about how to put back the default copy constructor. However, that's still irrelevant to the linker error.

Well, the "undefined reference" should be easily solved by linking in the implementation of the function!
NOTE: as things stand, returning a copy will trigger a stack overflow.
EDIT: IMO, your design is flawed, you have moved the construction logic outside of A into convert; rather than this, you should provide specific conversion constructors in A. It's either that or some boost::enable_if trick to disable the conversion constructor if the passed in type is derived from A (you can use one of the type_traits for that). For example, if you can construct an A from an int, provide the specific constructor in A itself.

Related

Why doesn't the compiler generate compile errors if an incorrect argument type is passed to a struct initialiser list?

I have defined a struct, which has a constructor:
struct MyStruct
{
MyStruct(const int value)
: value(value)
{
}
int value;
};
and the following objects:
int main()
{
MyStruct a (true);
MyStruct b {true};
}
But I haven't received any compile errors, either with MVS2015 or Xcode 7.3.1.
Why am I not getting any compile errors?
How do I make the compiler help me detect this? (Initially, the struct was written to have bool data, but after some time, code changed and bool became int and several bugs were introduced.)
A bool can be implicitly converted to an int in a way that's value preserving. The only disallowed conversions with brace initialization are narrowing conversions (e.g. the reverse bool{42}).
If you want to ensure that your class is constructible only with int, then the direct way is simply to delete all the other constructors:
struct MyStruct
{
explicit MyStruct(int i) : value(i) { }
template <typename T>
MyStruct(T t) = delete;
int value;
};
Here, MyStruct{true} and MyStruct(false) will yield calls to MyStruct::MyStruct<bool>, which is defined as deleted and hence is ill-formed.
The advantage of this over static_assert is that all the type traits will actually yield the correct values. For instance, std::is_constructible<MyStruct, bool> is std::false_type.
Here's a construction that allows you to only initialize your class from an int value:
#include <type_traits>
struct MyStruct
{
template <typename T>
MyStruct(T t) : value(t)
{
static_assert(std::is_same<T, int>::value, "Bad!");
}
int value;
};
That's because the template argument deduction required by this constructor template will produce the exact type of the argument and not perform conversions, so you can perform tests on that type.
You should perhaps also or instead use SFINAE to constrain the constructor, so that MyStruct doesn't present itself as constructible from anything.
Furthermore, you should probably also make the constructor template explicit so that random integers don't become MyStruct instances.
In other words, I'd write it like so:
struct MyStruct
{
template <typename T,
typename = std::enable_if_t<std::is_same<T, int>::value>>
MyStruct(T t) : value(t) {}
// ...
The simplest solution is to declare a bool constructor as deleted isn't it?
struct MyStruct
{
MyStruct(bool) = delete;
MyStruct(const int value)
: value(value)
{
}
int value;
};
example error output:
...
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:68:14: error: call to deleted constructor of 'MyStruct'
MyStruct b {true};
^ ~~~~~~
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:57:9: note: 'MyStruct' has been explicitly marked deleted here
MyStruct(bool) = delete;
^
2 errors generated.
Because a bool can be implicitly converted to an int.
This is a language feature that you cannot turn off, sorry.
the value true is converted to value 1 (int).

Constructor design for a class which delegates a value to one of its member variables

Please consider the following tree class
template<typename T>
class tree
{
public:
template<typename U>
tree(U&& value)
: m_value(std::forward<U>(value))
{ }
private:
T m_value;
};
and some verbose class foo which is constructed by its std::string name which is printed via std::cout whenever one of its constructors is invoked.
foo a("a"), b("b");
foo const c("c");
tree<foo> s(std::move(a)), t(b), u(c);
yields the following output:
a constructed
b constructed
c constructed
a moved
b copied
c copied
which is as expected.
How does this work?
To be specific: We cannot use
tree(T&& value)
: m_value(std::forward<T>(value))
{ }
Why? Well, cause we force decltype(value) to be foo&& which is not the case in t(b) and u(c). We would need to provide
tree(T const& value)
: m_value(value)
{ }
as well, if we want to use this arguable variant. The ctor used in the tree implementation works cause of the reference collapsing rules.
But why can't we use
template<typename U>
tree(U&& value)
: m_value(std::forward<T>(value))
{ }
instead?
Well, the reason is that in u(c) we have U = foo const& = decltype(value), but std::forward<T> only accepts foo& or foo&&, since T = foo. In contrast, std::forward<U> accepts foo const& (or foo const&&), since U = foo const&.
So, if I'm not overlooking something, we should use template<typename U> tree(U&&) instead of a tree(T const&), tree(T&&) combination. But: The compiler will take the template<typename U> tree(U&&) ctor in push_back, where the intend is that the move ctor of tree is taken:
foo a("a"), b("b");
foo const c("c");
tree<foo> t("root");
t.emplace_back(std::move(a));
t.emplace_back(b);
t.emplace_back(c);
tree<foo> u("other root");
u.push_back(t); // cannot convert argument 1 from 'tree<foo>' to 'std::string const&'
What can I do? Do I need to use the tree(T const&), tree(T&&) combination instead?
[Sorry for being a bit too verbose, but I felt responsible for clarifying any technical issue before asking for design.]
A greedy constructor such as template<typename U> tree(U&&) must be suitably constrained, or you'll have a lot of problems down the road. You saw it hijack copy construction from a non-const lvalue because it's a better match than the copy constructor. It also defines an implicit conversion from everything under the sun, which can have "fun" effects on overload resolution.
A possible constraint might be "accept only things that are convertible to T":
template<typename U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
tree(U&& u) : m_value(std::forward<U>(u)) {}
Or perhaps "accept only things that are convertible to T and not a tree".
template<class U>
using not_me = std::negation<std::is_same<U, tree>>;
template<typename U,
class = std::enable_if_t<std::conjunction_v<not_me<std::decay_t<U>>,
std::is_convertible<U, T>>>>
tree(U&& u) : m_value(std::forward<U>(u)) {}
You can do both.
If you go with:
template<typename U>
tree(U&& value)
: m_value(std::forward<U>(value))
{ }
the downside is that any one-argument constructor of T can be invoked this way. This may not be what you want. For example, with that variant, the following is valid:
struct foo
{
foo() = default;
foo(const foo &ref) = default;
explicit foo(int);
};
tree<foo> t(10);
t = 20;
This is a decision you need to find for yourself, however, I personally see that as a huge downside. I would make that constructor explicit (eliminating the second initialisation) and go for tree(const T&) along with tree(T&&) to avoid the first initialisation.

Call to template base constructor is ambiguous

The following code
template<class T>
struct Bar
{
Bar(T& myT){}
Bar(const Bar&) = delete;
};
template<class T>
struct Foo: public T,
public Bar<T>
{
Foo(): Bar<T>(*this){}
};
class Baz{};
int main()
{
Foo<Baz> myFoo;
return 0;
}
Gives me this error:
error: call to constructor of 'Bar<Baz>' is ambiguous
How can I fix this?
(Seems simple, I'm sure there's a duplicate somewhere, but I couldn't find it... all questions I found with "ambiguous constructor" stuff had to do with overloaded constructors, and this seems different to me.)
Deleted constructors participate in overload resolution. This is in order to ensure that the compilation really fails if a program attempts to use a deleted constructor. See this answer for more details.
The relevant section in the C++11 standard is 8.4.3/2:
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
[ Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member
to the function. It applies even for references in expressions that are not potentially-evaluated. If a function
is overloaded, it is referenced only if the function is selected by overload resolution. —end note ]
You can solve your problem by making the constructor call unambiguous:
template<class T>
struct Foo: public T,
public Bar<T>
{
Foo(): Bar<T>(static_cast<T &>(*this)){}
};
You have two constructors in Bar<Baz>:
Bar(Baz& );
Bar(const Bar& );
The fact that the second is deleted doesn't matter for the purposes of overload resolution. You are trying to construct it from a Foo<Baz>&... which is both a Baz and a Bar<Baz>, so both overloads apply - and the compiler can't prefer one over the other, so gives you the ambiguous error. Here's a simpler example with no templates that demonstrates the same issue:
struct A { };
struct B { };
struct C : A, B { };
void foo(A& ) { }
void foo(B& ) { }
int main() {
C c;
foo(c); // error: call of overloaded ‘foo(C&)’ is ambiguous
}
To break the ambiguity, could just explicitly tell the compiler which overload to use with casting:
Foo(): Bar<T>(static_cast<T&>(*this)) {} // will call Bar(Baz&)

Error converting form boost::shared_ptr<T> to std::shared_ptr<T>

I wrote a function template to "convert"/repack a boost::shared_ptr<T> to a std::shared_ptr<T> and vice versa by following this proposal. It's working fine unless I have a boost::shared_pt<T> and the type of T is an abstract class.
What I figured out so far is, that the problem occurs when boost/shared_ptr.hpp and boost/shared_array.hpp are included together. If only boost/shared_ptr.hpp is included it's working when the type of T is an abstract class.
I'm using clang 3.3 and boost 1.55.0 . It would be great if someone could tell my why it's not working and how to get it working.
Thanks for your help
Here is a minimal example:
//main.cpp
#include <boost/shared_array.hpp> //removing this include and it's working
#include <boost/shared_ptr.hpp>
#include <memory>
template<typename SharedPointer> struct Holder {
SharedPointer p;
Holder(const SharedPointer &p) : p(p) {}
Holder(const Holder &other) : p(other.p) {}
Holder(Holder &&other) : p(std::move(other.p)) {}
void operator () (...) const {}
};
template<class T>
std::shared_ptr<T> to_std_ptr(const boost::shared_ptr<T> &p)
{
typedef Holder<std::shared_ptr<T>> H;
if(H *h = boost::get_deleter<H, T>(p)) // get_deleter seems to cause the problem
{
return h->p;
}
else
{
return std::shared_ptr<T>(p.get(), Holder<boost::shared_ptr<T>>(p));
}
}
Here the code I used to test it:
//main.cpp
template<typename T> class Base
{
public:
T value;
virtual void abstract() = 0;
virtual ~Base() {}
};
template<typename T> class Derived : public Base<T>
{
public:
virtual void abstract() override {}
virtual ~Derived() {}
};
int main(int argc, const char * argv[])
{
boost::shared_ptr<Base<int>> ptr{new Derived<int>()};
// error here
std::shared_ptr<Base<int>> a = to_std_ptr(ptr);
// no error here
std::shared_ptr<Base<int>> b = to_std_ptr(boost::static_pointer_cast<Derived<int>>(ptr));
return 0;
}
Here's the error message(shortened):
boost/smart_ptr/shared_array.hpp:111:102: error: array of abstract class type 'Base<int>'
shared_array( shared_array<Y> const & r, typename boost::detail::sp_enable_if_convertible< Y[], T[] >::type = boost::detail::sp_empty() )
main.cpp:64:40: note: in instantiation of template class 'boost::shared_array<Base<int> >' requested here
if(H *h = boost::get_deleter<H, T>(p))
main.cpp:86:36: note: in instantiation of function template specialization 'to_std_ptr<Base<int> >'requested here
std::shared_ptr<Base<int>> i = to_std_ptr(ptr);
main.cpp:23:18: note: unimplemented pure virtual method 'abstract' in 'Base'
virtual void abstract() = 0;
What I get from the error message is that the compiler tried to create an array of abstract classes what of course doesn't work. But why is he even trying to do so and what has boost/sharred_array to do with that. Is he maybe picking the wrong overload for boost::get_deleter?
Here's a small example of where the error comes from:
struct abstract
{
virtual void foo() = 0;
};
template<class X, class Y>
struct templ {};
template<class T>
struct bar
{
template<class U>
bar(templ<U[], T[]>) {} // (A)
};
int main()
{
bar<abstract> x;
}
It seems even to be illegal to form the type array of [abstract-type] in (A), therefore instantiating the class template bar with the argument abstract makes the program ill-formed.
The same thing is happening in the background for shared_array. But why is shared_array<Base> instantiated?
The free function boost::get_deleter is overloaded, shared_array.hpp adds an overload to the overload set (actually, adds a template):
template< class D, class T > D * get_deleter( shared_array<T> const & p );
Before overload resolution, even before finding out which functions are viable, function templates need to be instantiated. Instantiating this get_deleter template above leads to instantiating shared_array<Base>, which leads to the program being ill-formed.
The solution is, not to let the above get instantiated: Don't supply the template parameter T, it can't deduce the T in shared_array<T> from a shared_ptr<T>: the two types are unrelated.
Change
if(H *h = boost::get_deleter<H, T>(p))
to
if(H *h = boost::get_deleter<H>(p))
and it works.
Explanation why letting T be deduced works:
When writing a function call where a function template could be meant (looking at the name called), the template parameters have to be set. You can supply them explicitly (inside <> as in get_deleter<H, T>). If you don't supply all of them explicitly (as in get_deleter<H>), the remaining ones have to be deduced from the arguments of the function call.
After all template parameters have been either set explicitly or deduced, their occurrences in the function template are substituted. The error when using get_deleter<H, Derived> occurs in this step: the substituted get_deleter looks like this:
template<> H * get_deleter( shared_array<Derived> const & p );
Here, shared_array<Derived> needs to be instantiated. But during this instantiation, the error explained above occurs. (Note: it's not in the immediate context of get_deleter, therefore SFINAE doesn't apply.)
Now, when you don't explicitly supply the second template parameter, it has to be deduced. And this deduction fails for the function template
template< class D, class T > D * get_deleter( shared_array<T> const & p );
if you use an argument expression of type shared_ptr<Derived>. As deduction fails, no instantiation takes place of the function template, and therefore no instantiation of shared_array<Derived> (deduction fails = some template parameters could not be set).
Why does deduction fail? The compiler needs to deduce the template parameter T inside the function parameter type shared_array<T> const& from the argument expression, which is of the type shared_ptr<Derived>. But this deduction can only succeed (with few exceptions) when the function parameter type can be made equal to the argument expression type. I.e., it can only succeed if there's some type X, such that shared_array<X> is the same type as shared_ptr<Derived>. But there isn't: the specializations of shared_ptr and shared_array are unrelated.
Therefore, the template parameter T of this get_deleter overload cannot be deduced; therefore this function template isn't instantiated, and shared_array<Derived> isn't instantiated.
Deduction failure is a special kind of failure (like SFINAE): It doesn't lead to the program being ill-formed (i.e. it doesn't lead to a compilation error). Rather, the function template for which deduction didn't succeed simply doesn't add a function to the overload set. If there are other functions in the overload set, one of those can be called instead.
Another function template
template<class D, class T> D * get_deleter( shared_ptr<T> const & p )
from boost/smart_ptr/shared_ptr.hpp runs through the same process. However, deduction succeeds here, and a specialization get_deleter<H, T> (with the H and T from to_std_ptr) is added to the overload set. It'll later be chosen by overload resolution.

conversion operator with template functions

I have a class with a conversion operator to std::string. It works great with everything except with functions receiving std::basic_string<T> (templated on T).
#include <string>
struct A{
operator std::string(){return std::string();}
};
void F(const std::basic_string<char> &){}
template<typename T> void G(const std::basic_string<T> &) {}
int main(){
A a;
F(a); // Works!
G(a); // Error!
return 0; // because otherwise I'll get a lot of comments :)
}
The error I receive is
error: no matching function for call to 'G(A&)'
note: candidate is:
note: template<class T> void G(const std::basic_string<_CharT>&)
Now, I know I can define G as a friend in the struct A and it'll work, but my problem is with a lot of stl functions that already exist and receive std::basic_string<T> (for example, the operator<< printing function, or comparison operators, or many other functions.
I would really like to be able to use A as if it was an std::string. Is there any way to do this?
I would really like to be able to use A as if it was an std::string. Is there any way to do this?
Yes, but are you sure you really want this? The solution is:
struct A : public std::string {
};
but recall that std::string doesn't have a virtual destructor and therefore, cannot be used polymorphically. You have been warned!!!
A str() is a far better solution and allows you to be explicit when you want to pass your A to a function taking a std::basic_string<T>.
The compiler cannot infer that far; you'll either have to explicitly call the cast operator or to explictly specify the template parameter :
G(static_cast<std::string>(a));
G<char>(a);
To understand why the compiler can't do both user-defined conversion and template argument deduction, let's take this example :
template<typename T>
struct Number {
Number(double n) {};
Number(int n) {};
};
struct A{
operator Number<double>(){return Number<double>(1.);}
operator Number<int>(){return Number<int>(1);}
};
template<typename T> void G(Number<T>& number) { }
int main(){
A a;
G(a); // What do I do ?!
return 0;
}
What the compiler should do in that case ?
User defined conversions are not taken into consideration when performing template argument deduction.
Explicit specialization of G will work.
G<char>(a);