2014-07-01 51 views
2

我得到一个编译器错误铿锵++。 MSVC++很高兴。我相信我的声明是正确的。问题朋友模板函数铿锵++/msvC++和enable_if

我的信念不正确,我在MSVC中“幸运”? 是否有一个非#ifndef _MSC_VER ... public:的方式使这两个编译器的工作?

我想保持构造函数为私有。真正的代码稍微复杂一些。 (额外的模板元编程和“完美转发”)下面是问题的蒸馏版本,并尽可能地隔离问题。我已经为朋友声明尝试了许多变体。示例中显示了“似乎最好”的那个。

#include<type_traits> 

template<typename T> class Wrap; 

template<typename T, 
    typename std::enable_if<std::is_class<T>::value, T>::type* = nullptr > 
Wrap<T> make_wrapper(T i) 
{ 
    return Wrap<T>(i); 
} 

template<typename T> 
class Wrap : T 
{ 
    friend Wrap<T> make_wrapper<T,nullptr>(T); 
private: 
    Wrap(T v) : T(v) {} 
}; 

template<typename T> 
class Imp 
{ 
    T x; 
public: 
    Imp(T v) {} 
}; 

int main() 
{ 
    auto wrap = make_wrapper(Imp<int>(1)); 
    return 0; 
} 

铛++:

$ clang++ --version 
Debian clang version 3.5-1~exp1 (trunk) (based on LLVM 3.5) 
Target: x86_64-pc-linux-gnu 
Thread model: posix 

$ clang++ -std=c++11 test.cpp 
test.cpp:8 : 12 : error : calling a private constructor of class 'Wrap<Imp<int> >' 
return Wrap<T>(i); 
^ 
test.cpp:30 : 17 : note : in instantiation of function template specialization 'make_wrapper<Imp<int>, nullptr>' requested here 
auto wrap = make_wrapper(Imp<int>(1)); 
^ 
test.cpp:16 : 5 : note : declared private here 
Wrap(T v) : T(v) { } 
^ 
1 error generated. 

CL

Microsoft Visual Studio Professional 2013 
Version 12.0.30501.00 Update 2 

1>------ Build started: Project: Test1, Configuration: Debug Win32 ------ 
1> Test1.cpp 
1> Test1.vcxproj -> C:\cygwin64\home\username\Test1\Debug\Test1.exe 
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== 
+0

FWIW,g ++ 4.8.2认为你的代码是好的。 –

+0

谢谢我应该检查g ++以及相关代码。如果我不让太多额外的怪癖渗透到代码中,我怀疑它将为我节省时间。到目前为止,我一直在避免花费太多时间。我想现在是时候将g ++添加到列表中。 – ErnieE

回答

1

模板的朋友们notoriously complicated。我不知道肯定锵是否是正确的,但它可能是功能模板参数里面你SFINAE招触犯运行的

14.5.4好友[temp.friend]

9当朋友声明涉及功能 模板的专业化时,函数参数声明不应包含 默认参数,也不应在此类声明中使用内联说明符。

C++ 11引入了函数模板的默认模板参数,它可能是铿锵将上述不同解释为g ++/MSVC。它是由返回类型做SFINAE而是可以解决的:

#include<type_traits> 

template<typename T> class Wrap; 

template<typename T> 
using WrapRet = typename std::enable_if<std::is_class<T>::value, Wrap<T>>::type; 

template<typename T> 
WrapRet<T> make_wrapper(T i) 
{ 
    return Wrap<T>(i); 
} 

template<typename T> 
class Wrap : T 
{ 
    friend WrapRet<T> make_wrapper<T>(T); 
private: 
    Wrap(T v) : T(v) {} 
}; 

template<typename T> 
class Imp 
{ 
    T x; 
public: 
    Imp(T v) {} 
}; 

int main() 
{ 
    auto wrap = make_wrapper(Imp<int>(1)); 
    return 0; 
} 

Live Example与两个铛和g ++工程。

+0

当然,它会为简单的孤立案件工作!感谢您的想法!在非隔离代码的“真实世界”中,我避免将声明移入返回类型或作为参数,因为生成的代码变得更难以阅读。当我尝试(刚才)将enable_if移动到返回类型时,我得到了其他有趣的错误。 (我会在稍后讨论这些问题。)我曾经喜欢模板,但是C++ 11模板元语法可能有点多。 – ErnieE

+0

@ErnieE有一些帮助器类型,比如'WrapRet'可读性应该保持OK-ish。期待减少的测试用例,如果你真的仍然无法工作 – TemplateRex

+0

使用'使用'是唯一使事情变得糟糕的事情(国际海事组织:记得我真的很喜欢模板)。私人构造函数现在可以工作,但我没有时间来追查什么正在绊倒另一个我想保持私有的成员。我不知道enable_if构造会在助手中起作用。在不久的将来,我将明确地转移其他一些宣言(如适用)。 **如果**您对该项目感兴趣,请点击[链接] https://github.com/ErnieE5/ee5_util/blob/master/inc/marshaling。h 感谢您的帮助。 – ErnieE