c++ - 模板特化和DLL:Visual Studio与(GCC/Clang)

标签 c++ templates visual-studio-2012 gcc clang

这个问题很难描述。我有一个跨平台的应用程序。目前,我正在使用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

    最佳答案

    这里的问题是MSVC和GCC / Clang确实以不同方式解析模板。在此处查看标记为“巨大编辑”的帖子:Overload Resolution in a Namespace

    我在GitHub上发布了一个示例项目来说明问题。通过更改模板重载在最终可执行文件中的定义顺序(包括该重载),可以解决我测试过的所有编译器上的错误。本质上:

    // First
    #include <original set of templates and specializations>
    
    // Next
    // Define new specialization.
    
    // Then
    #include <class that uses the specialization, possibly from another assembly>
    
    // Finally
    // Code that uses the #include'ed class that uses the specialization.
    

    我将更新github项目以显示“之前”和“之后”

    关于c++ - 模板特化和DLL:Visual Studio与(GCC/Clang),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34356871/

    相关文章:

    c++ - 在 OMNeT++ 中定期添加主机

    c++ - 查找数组重复单元的最简单方法是什么?

    c++ - 为什么不从构造函数推断模板参数?

    symfony - 我可以将 Blade 模板引擎(laravel)与 symfony 一起使用吗?

    c++ - Visual Studio 2012 找不到 shlwapi.lib

    c++ - 对openGL的摄像头和摄像头空间转换的困惑

    c++ - 既然可以使用函数引用,为什么还要使用仿函数

    c++ - 可变参数模板示例未编译

    c# - 有条件地检查拨动开关 c#

    visual-studio - 如何配置 Visual Studio 以便多个实例在任务栏中具有单独的图标?