【问题标题】:Specialization of template variable (for template template class)模板变量的特化(用于模板模板类)
【发布时间】:2016-01-27 02:38:12
【问题描述】:

当我尝试为通用容器(例如std::list<...>,而不是特定容器,例如std::list<double>)专门化一个模板变量时,我收到gcc 5.3 的链接错误(但不是clang 3.5

/tmp/ccvxFv3R.s: Assembler messages:
/tmp/ccvxFv3R.s:206: Error: symbol `_ZL9separator' is already defined

http://coliru.stacked-crooked.com/a/38f68c782d385bac

#include<string>
#include<iostream>
#include<list>
#include<forward_list>
#include<vector>

template<typename T> std::string const separator = ", ";
template<typename... Ts> std::string const separator<std::list<Ts...>        > = "<->";
template<typename... Ts> std::string const separator<std::forward_list<Ts...>> = "->";

int main(){

    std::cout << separator<std::vector<double>> << '\n';
    std::cout << separator<std::list<double>> << '\n';
    std::cout << separator<std::forward_list<double>> << '\n';

}

(这可以很好地与clang 3.5 一起编译并按预期工作。可变参数模板也不是导致问题的原因,我尝试使用非可变参数模板)。

如果这不是 gcc 中的错误,您认为有解决方法吗?我尝试使用类专业化,但也不可能:

template<class T>
struct separator{
    static std::string const value;
};
template<class T>
std::string const separator<T>::value = ", ";
template<typename... Ts>
std::string const separator<std::list<Ts...>>::value = "<->";
template<typename... Ts>
std::string const sep<std::forward_list<Ts...>>::value = "->";

【问题讨论】:

  • 我认为 GCC 对变量模板部分特化的处理被打破了。类模板更成熟,但你必须对整个东西进行部分专业化,这很烦人。
  • @T.C.是的,我最终这样做了,非常烦人。谢谢。

标签: c++ gcc c++14 template-specialization variable-templates


【解决方案1】:

这似乎是gcc 的问题。解决方法(使用类模板),例如 @T.C.建议。

template<class T>
struct sep{
    static const std::string value;
};
template<class T>
const std::string sep<T>::value = ", ";

template<typename... Ts>
struct sep<std::list<Ts...>>{
    static const std::string value;
};
template<typename... Ts>
const std::string sep<std::list<Ts...>>::value = "<->";

template<typename... Ts>
struct sep<std::forward_list<Ts...>>{
    static const std::string value;
};
template<typename... Ts>
const std::string sep<std::forward_list<Ts...>>::value = "->";

后来一个模板变量(所以有相同的接口)

template<typename T> std::string const separator = sep<T>::value;

这适用于gccclang


或者@T.C.也建议,使用静态函数成员而不是静态成员(更少代码)

template<class T>
struct sep{
    static std::string value(){return ", ";}
};
template<typename... Ts>
struct sep<std::list<Ts...>>{
    static std::string value(){return "<->";}
};

template<typename... Ts>
struct sep<std::forward_list<Ts...>>{
    static std::string value(){return "->";}
};
...
template<typename T> std::string const separator = sep<T>::value();

或使用constexpr const char*

template<class T>
struct sep{static constexpr const char* value = ", ";};

template<typename... Ts>
struct sep<std::list<Ts...>>{static constexpr const char* value = "<->";};

template<typename... Ts>
struct sep<std::forward_list<Ts...>>{static constexpr const char* value = "->";};
...
template<typename T> std::string const separator = sep<T>::value;

我尝试使用const_strconstexpr 友好版本的std::string)但出现奇怪的链接器错误。

【讨论】:

  • 此时你还不如只使用静态成员函数而不是静态数据成员。 (或者使用static constexpr const char*;两者都不需要外线定义。)
  • @T.C.,好点子。我将其添加到我的答案中。它的代码更少。我不明白为什么const static 成员不能内联(类内)定义,毕竟现在甚至(非静态)成员也可以内联定义。 (constexpr std::string 是不可能的)。该功能将使静态成员更具吸引力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
相关资源
最近更新 更多