gcc 中的错误,或 clang/MSVS 中的扩展
Bug in gcc, or extention in clang/MSVS
以下代码片段可以在 clang 和 MSVS 中编译,但不能在 gcc 中编译。
template<typename T> class clone_ptr;
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args );
// note: everything not needed for example cut out, so
// this class is neither complete nor correct
template<typename T>
class clone_ptr
{
public:
clone_ptr() : ptr(nullptr) {}
operator bool() { return ptr!=nullptr; }
T* operator->() { return ptr; }
private:
clone_ptr(T* p) : ptr(p) {}
T* ptr;
template<class T1,class U1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
};
template<typename T, typename U=T, typename ...Args>
clone_ptr<T> make_cloned( Args ...args )
{
return {new U(args...)};
}
// ----------------------------------------------
#include <string>
#include <vector>
#include <iostream>
using namespace std;
struct Base
{
int a;
Base( int a=0 ) : a(a) {}
virtual string foo() { return "Base "+to_string(a); };
virtual ~Base() {}
};
struct Sub : Base
{
Sub( int a=0 ) : Base(a) {}
virtual string foo() override { return "Sub "+to_string(a); };
};
string testit()
{
std::vector< clone_ptr< Base > > vec;
vec.push_back( make_cloned<Base>(7) );
vec.emplace_back();
vec.push_back( make_cloned<Base,Sub>(5) );
string ss;
for( auto&& a : vec )
{
ss += a?a->foo():"<empty>";
}
return ss;
}
int main()
{
cout << testit() << endl;
}
gcc 抱怨:
error: no matching function for call to 'make_cloned(int)'
vec.push_back( make_cloned<Base>(7) );
note: candidate is:
note: template<class T, class U, class ... Args> clone_ptr<T> make_cloned(Args ...)
clone_ptr<T> make_cloned( Args ...args )
^
note: template argument deduction/substitution failed:
note: couldn't deduce template parameter 'U'
vec.push_back( make_cloned<Base>(7) );
这是 gcc 中的错误吗?是否有解决方法,仅
依赖于符合标准的 C++ ?
确实,这似乎是一个错误。解决方法是将默认模板参数分离到第二个函数中。在 clone_ptr
内,您有两个朋友:
template<class T1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
template<class T1, class U1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
而且定义很简单:
template<typename T, typename ...Args>
clone_ptr<T> make_cloned( Args ...args ) { return {new T(args...)}; }
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; }
已使用 gcc 4.8.3 和 clang 3.5 进行测试。
编辑:
经过调查,我能够以两种不同的方式让您的代码与 gcc 4.8.3 一起工作:
完全删除模板函数声明
// this is not needed:
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args );
将默认模板参数定义从模板函数定义移动到声明中:
template<typename T, typename U = T, typename ...Args>
clone_ptr<T> make_cloned( Args ...args );
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args )
{
return {new U(args...)};
}
我仍然认为这是 gcc 的问题,但这样你的代码就可以工作了。
以下代码片段可以在 clang 和 MSVS 中编译,但不能在 gcc 中编译。
template<typename T> class clone_ptr;
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args );
// note: everything not needed for example cut out, so
// this class is neither complete nor correct
template<typename T>
class clone_ptr
{
public:
clone_ptr() : ptr(nullptr) {}
operator bool() { return ptr!=nullptr; }
T* operator->() { return ptr; }
private:
clone_ptr(T* p) : ptr(p) {}
T* ptr;
template<class T1,class U1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
};
template<typename T, typename U=T, typename ...Args>
clone_ptr<T> make_cloned( Args ...args )
{
return {new U(args...)};
}
// ----------------------------------------------
#include <string>
#include <vector>
#include <iostream>
using namespace std;
struct Base
{
int a;
Base( int a=0 ) : a(a) {}
virtual string foo() { return "Base "+to_string(a); };
virtual ~Base() {}
};
struct Sub : Base
{
Sub( int a=0 ) : Base(a) {}
virtual string foo() override { return "Sub "+to_string(a); };
};
string testit()
{
std::vector< clone_ptr< Base > > vec;
vec.push_back( make_cloned<Base>(7) );
vec.emplace_back();
vec.push_back( make_cloned<Base,Sub>(5) );
string ss;
for( auto&& a : vec )
{
ss += a?a->foo():"<empty>";
}
return ss;
}
int main()
{
cout << testit() << endl;
}
gcc 抱怨:
error: no matching function for call to 'make_cloned(int)'
vec.push_back( make_cloned<Base>(7) );
note: candidate is:
note: template<class T, class U, class ... Args> clone_ptr<T> make_cloned(Args ...)
clone_ptr<T> make_cloned( Args ...args )
^
note: template argument deduction/substitution failed:
note: couldn't deduce template parameter 'U'
vec.push_back( make_cloned<Base>(7) );
这是 gcc 中的错误吗?是否有解决方法,仅 依赖于符合标准的 C++ ?
确实,这似乎是一个错误。解决方法是将默认模板参数分离到第二个函数中。在 clone_ptr
内,您有两个朋友:
template<class T1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
template<class T1, class U1, typename ...Args>
friend clone_ptr<T1> make_cloned( Args ...args );
而且定义很简单:
template<typename T, typename ...Args>
clone_ptr<T> make_cloned( Args ...args ) { return {new T(args...)}; }
template<typename T, typename U, typename ...Args>
clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; }
已使用 gcc 4.8.3 和 clang 3.5 进行测试。
编辑: 经过调查,我能够以两种不同的方式让您的代码与 gcc 4.8.3 一起工作:
完全删除模板函数声明
// this is not needed: template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args );
将默认模板参数定义从模板函数定义移动到声明中:
template<typename T, typename U = T, typename ...Args> clone_ptr<T> make_cloned( Args ...args ); template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; }
我仍然认为这是 gcc 的问题,但这样你的代码就可以工作了。