【发布时间】:2015-12-18 13:30:50
【问题描述】:
这个问题描述起来很复杂。我有一个跨平台的应用程序。目前我正在使用 Visual Studio 2012、GCC 4.8.x 和 Clang 3.5。该应用程序由许多动态库组成,因此问题的一部分通过各种程序集传播。
我看过这些堆栈溢出问题,但它们似乎没有解决这个问题:
- Clang can't handle a template specialization using referenced template template, but GCC can
- template specialization, different behavior on windows vs gcc?
- Template specialization and instantiation
- Template specialization and enable_if problems
- Explicit template specialization in g++ causing troubles
我有一小群模板化的函数,每个函数都有一个独特的专业化(主要使用类型特征)。这些模板化函数接受类型并将它们转换为特殊字符串 (std::string ToString(T x)),然后接受这些字符串并将它们转换回它们的类型 (T StringTo<T>(std::string x))。我有其他使用这些特殊转换的模板类。
一些动态库(插件)可能想要为它们添加新的类型和转换。伟大的。为了解决这个问题,我写了一个邪恶的宏(虽然这个问题根本没有宏就存在,这里只是完全公开。)
问题
在 Visual Studio 2012 上编译时,一切正常。 切换到 GCC/Clang,事情开始变糟。如果我保持实现相同,我会遇到编译器无法为新类型找到匹配重载的问题。我试图#ifdef WIN32 摆脱这种情况,但没有运气。我找不到让我获得新模板化重载的路径。
我怀疑这可能是因为 GCC/Clang 不能很好地处理在库之间传播的模板特化(这对于编译器编写者来说似乎是一个相当困难的问题)......但我不想接受失败。我什至尝试在 Clang(-fms-compatibility 和 -fdelayed-template-parsing)上设置编译器选项,但没有成功。 (http://clang.llvm.org/docs/MSVCCompatibility.html)
编译器输出
这是编译器输出的示例,但名称已更改以保护无辜者并与下面的伪代码相关。此消息有很多变体(基本上每个专业都有一个):
/path/utilities/ToString.h:1014:15: note: template argument deduction/substitution failed:
/path/utilities/ToString.h: In substitution of ‘template<class T> std::string ToString(const T&, typename std::enable_if<((std::is_same<T, char>::value || std::is_same<T, signed char>::value) || std::is_same<T, unsigned char>::value), T>::type*) [with T = Bar]’:
/path/core/Foo.h:156:49: required from ‘std::string Foo<T>::getValue() const [with T = Bar; std::string = std::basic_string<char>]’
/path/plugin/Bar.cpp:201:1: required from here
/path/utilities/ToString.h:1014:15: error: no type named ‘type’ in ‘struct std::enable_if<false, Bar>’
/path/core/Foo.h: In instantiation of ‘std::string Foo<T>::getValue() const [with T = Bar; std::string = std::basic_string<char>]’:
/path/plugin/Bar.cpp:201:1: required from here
/path/utilities/ToString.h:1029:15: note: template<class T> std::string ToString(const T&, typename std::enable_if<(((std::is_convertible<T*, std::basic_string<char> >::value && (! std::is_same<T, char>::value)) && (! std::is_same<T, signed char>::value)) && (! std::is_same<T, unsigned char>::value)), T>::type*)
std::string ToString(const T& x, typename std::enable_if<std::is_convertible<T*, std::string>::value && !std::is_same<T, char>::value && !std::is_same<T, signed char>::value && !std::is_same<T, unsigned char>::value, T>::type*)
^
伪代码
我经历了很多次尝试移动定义,将模板定义与声明分开,编译器标志......这一切都是可怕的模糊。
这里有一些代码来演示我在做什么:
Utilities.dll、ToString.h
// Prototype some templates (Makes GCC/Clang Happy. Visual Studio did not require.)
template<typename T> typename std::enable_if<std::is_constructible<T, std::string>::value, T>::type StringTo(std::string x);
template<typename T> typename std::enable_if<std::is_same<T, float>::value, T>::type StringTo(std::string x);
template<typename T> typename std::enable_if<std::is_same<T, double>::value, T>::type StringTo(std::string x);
// ...and many more.
// Implement the templates
template<typename T>
typename std::enable_if<std::is_constructible<T, std::string>::value, T>::type StringTo(std::string x)
{
return T(x);
}
// ...and many more.
// Finally, define a macro to build more conversions from types later.
#define BUILD_MORE(V) \
template<typename T>\
typename std::enable_if<std::is_same<T, V>::value, T>::type StringTo(std::string x)\
{\
return specialConversionCodeHere;\
}
Core.dll(依赖于 Utilities.dll)、Foo.h
template<typename T>
class Foo
{
T getValue()
{
// Use our specialized template.
return StringTo<T>(this->value);
}
std::string value;
}
Plugin.dll(依赖于Core.dll)、Bar.cpp
// Define a new class.
class Bar...
// Now use the fancy macro to build a StringTo conversion for it.
BUILD_MORE(Bar)
// Now use said conversion
void foobar()
{
// Create the templated class (Foo) which uses a
// specialized template for class Bar conversion from string.
// Prepare for horrible build errors outside of Visual Studio
auto fb = new Foo<Bar>();
auto x = fb->getValue();
}
我添加了一个演示问题的 github 项目:https://github.com/DigitalInBlue/TemplateTest
【问题讨论】:
-
见this 答案。您应该在使用它的方法之前声明专门化。
-
在这个例子中,特化在
BUILD_MORE宏中声明,然后被新类Foo<Bar>使用。 -
在
getValue方法中使用并在其后声明。 Here 是更好的例子 - 请参阅带有ERROR: explicit specialization of sort(Array<String>)评论的 sn-p -
对我来说,
Foo<Bar>应该会导致Foo<T>模板的实例化,这发生在StringTo<Bar>的新特化由BUILD_MORE宏定义之后。我在这里想念什么?这个顺序对我来说看起来“正确”。 -
也许您应该尝试制作最小的示例来重现问题。我刚刚注意到,即使您提供的输出也抱怨
ToString函数,并且您的宏定义了StringTo。
标签: c++ templates visual-studio-2012 gcc clang