这个问题描述起来很复杂。我有一个跨平台的应用程序。目前我使用的是Visual Studio 2012,GCC 4.8.x和Clang 3.5。该应用程序由许多动态库组成,因此问题的各个部分通过各种程序集传播。
我已经查看了这些堆栈溢出问题,但它们似乎没有解决这个问题:
我有一小部分模板化函数,每个函数都有一个独特的特化(主要使用类型特征)。这些模板化函数接受类型并将它们转换为特殊字符串(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
答案 0 :(得分:0)
这里的问题是MSVC和GCC / Clang确实以不同方式解析模板。请看这里标有&#34;巨大编辑&#34;:Overload Resolution in a Namespace
的帖子我在GitHub上发布了一个示例项目来说明问题。通过更改最终可执行文件与包含模板重载的顺序,在我测试的所有编译器上解决了错误。基本上:
var threads = new Thread[] {
new Thread(() =>
{
for (int x = 0; x < 10000; x++)
{
Console.WriteLine("First :" + x);
}
}),
new Thread(() =>
{
for (int x = 0; x < 10000; x++)
{
Console.WriteLine("Second :" + x);
}
})
};
// start the threads
foreach (var t in threads)
t.Start();
// block the initial thread until the new threads are finished
foreach (var t in threads)
t.Join();
// Now the following line won't execute until both threads are done
Console.ReadKey();
我将更新github项目以显示&#34;之前&#34; &&#34;在&#34;
之后