Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ill-formed C++0x code or compiler bug?

In the following C++0x code I tried to clone an object by using a clone member function (if it exists) and falling back on a copy constructor:

struct use_copy_ctor {};
struct prefer_clone_func : use_copy_ctor {};

template<class T>
auto clone(T const* ptr, prefer_clone_func)
-> decltype(ptr->clone())
{ return ptr->clone(); }

template<class T>
auto clone(T const* ptr, use_copy_ctor)
-> decltype(new T(*ptr))
{ return new T(*ptr); }

struct abc {
  virtual ~abc() {}
  virtual abc* clone() const =0;
};

struct derived : abc
{
  derived* clone() const { return new derived(*this); }
};

int main()
{
  derived d;
  abc* p = &d;
  abc* q = clone(p,prefer_clone_func());
  delete q;
}

The idea is to use auto...->decltype(expr) to weed out ill-formed expressions as part of the template argument deduction (SFINAE) and to resolve a possible ambiguity between both clone function templates via partial ordering w.r.t. the second function parameter.

Unfortunately, GCC 4.5.1 doesn't accept this program:

test.cpp: In function 'int main()':
test.cpp:28:39: error: cannot allocate an object of abstract type
  'abc'
test.cpp:14:12: note:   because the following virtual functions are
  pure within 'abc':
test.cpp:16:16: note:        virtual abc* abc::clone() const
test.cpp:28:39: error: cannot allocate an object of abstract type
  'abc'
test.cpp:14:12: note:   since type 'abc' has pure virtual functions

Now, the question is, is this a compiler bug or was I wrong to assume that SFINAE applies here? I'd appreciate an answer with a solid reasoning.

Edit: If I change decltype(new T(*ptr)) to T* the code compiles because of overload resolution preferring the first function template in this case. But this defeats the purpose of having the expression as part of the function declaration. The purpose is to make the compiler kick the function out of the overload resolution set in case of an error.

like image 594
sellibitze Avatar asked Jan 28 '11 11:01

sellibitze


2 Answers

I had a very similar problem with MSVC, and it turned out that the compiler wouldn't recognize my co-variant return types in the virtual function. Try changing derived's definition of clone to returning an abc.

like image 121
Puppy Avatar answered Nov 02 '22 20:11

Puppy


I think I convinced myself that this is actually a compiler bug and filed a report. Let's see what happens. As a workaround decltype(new T(*ptr)) can be replaced with T*. The only difference is that the function template will stay part of the overload resolution set which is not a big issue in this case.

Edit: Btw, I was told that clang++ accepts the above code.

like image 1
sellibitze Avatar answered Nov 02 '22 20:11

sellibitze