在下面的代码示例中,我定义了一个DT类(我的默认类型),我希望将其作为任意模板的参数传递。在此示例中,我将DT作为std :: map的键和值参数传递。我实际上从未尝试实例化map
#include <iostream>
#include <map>
using namespace std;
class DT {};
template <typename T>
string f() {
return "foo";
}
int main() {
cout << f<map<DT,DT>>() << endl;
return 0;
}
使用g ++似乎可以正常工作。我什至尝试为所有四个映射参数(覆盖默认的比较器和分配器类型)传递DT。仍然有效。但是我担心这种技术可能会因其他模板或其他编译器而失败。所以我的问题是:这对于符合c ++ 11标准(及更高标准)的任何c ++编译器上的任何模板总是安全的吗?换句话说,只要您从未尝试实例化该模板,将完全不兼容的类型作为模板的参数传递始终是安全的吗?
如果您想知道为什么我想做这样的事情,那么我正在尝试设置一个类,用于存储与类型相关的配置字符串。它将具有以下两种方法:
template<typename T>
const string& get<T>() const;
template<typename T>
void set<T>(const string& value);
我的工作很大程度上令我满意。它具有几个不错的功能。例如,类型int,const int,int&,const int&等都被视为同一类型(这就是我想要的)。而且,您可以存储基类的配置字符串,如果找不到更特定的派生类型的条目,则派生类型以后可以检索该配置字符串。但是对于一个std :: map来说,我希望能够使用map
答案 0 :(得分:1)
不幸的是,我相信,该标准不能保证不会实例化std::map<DT, DT>
。 [temp.inst]/1仅指定
除非已明确实例化或明确指定了类模板专业化,否则当在需要完全定义的对象类型的上下文中引用专业化时或类类型的完整性影响语义时,将隐式实例化类模板专业化该程序。 […]
请注意,这仅告诉我们何时可以确保模板被实例化,如果不能进行实例化,则不能保证模板不会被实例化。 [temp.inst]/10仅对此提供保证
[…]函数模板,变量模板,成员模板,非虚拟成员函数,成员类,类模板的静态数据成员或constexpr if语句的子语句([stmt。如果],则除非需要此类实例化。 […]
请注意,此列表中没有类模板。因此,我认为,即使认为没有必要,编译器理论上也可以实例化std::map<DT, DT>
。如果使用std::map
作为键和值类型实例化模板DT
无效,那么您将遇到问题。我找不到有关使用不支持比较运算符的键类型实例化std::map
的任何保证。尽管我希望这基本上可以与任何实现一起使用,但我确实认为从理论上讲允许实现,例如,使用static_assert
检查密钥类型是否满足必要的要求。 [res.on.functions]/1似乎适用(重点是我):
在某些情况下(替换函数,处理函数,用于实例化标准库模板组件的类型的操作),C ++标准库取决于C ++程序提供的组件。 如果这些组件不符合要求,则本标准对实施没有任何要求。
因此,我认为,严格来说,该标准不能保证使用std::map<DT, DT>
会起作用……
如果您只是想使用std::map<DT, DT>
作为标记类型来表示特殊情况,我建议您不要使用std::map
,而是使用其他一些东西,例如:
template <typename, typename>
struct default_config_tag;
然后以default_config_tag<DT, DT>
或仅以DT
作为标签(不确定是否需要将参数作为具有两个类型参数的模板的实例)就足够了。
答案 1 :(得分:0)
您已经got the answer to your question,但是对于本文的读者来说,问题的上下文也同样有趣,因此我认为值得一提的是,您可以在诸如以下的用例中使用标记分发您自己的:
int
)或类型组(例如std::map<K, V>
(对于通用K
和V
)设置默认配置字符串< / li>
如果没有标签分派,这可能会很棘手,因为您可能不会部分专门化功能模板。
例如:
#include <map>
#include <string>
#include <iostream>
template <typename T>
class Config {
public:
static const std::string& get() { return Config::getString(); }
static void set(const std::string& value) { Config::getString() = value; }
Config(Config const&) = delete;
void operator=(Config const&) = delete;
private:
static std::string& getString() {
static std::string s(defaultString(dispatch_tag<T>{}));
return s;
}
template <typename U>
struct dispatch_tag {};
// Default string unless specified for specific types below.
template <typename U = T>
static constexpr std::string_view defaultString(dispatch_tag<U>) {
return "default";
}
// Default config strings for a select number of types.
static constexpr std::string_view defaultString(dispatch_tag<int>) {
return "default int";
}
template <typename K, typename V>
static constexpr std::string_view defaultString(
dispatch_tag<std::map<K, V>>) {
return "default map";
}
};
int main() {
std::cout << Config<int>::get() << "\n"; // default int
std::cout << Config<std::string>::get() << "\n"; // default
std::cout << Config<std::map<int, int>>::get() << "\n"; // default map
Config<int>::set("custom int");
Config<std::map<int, int>>::set("custom int-int map");
std::cout << Config<int>::get() << "\n"; // custom int
std::cout << Config<std::map<int, int>>::get() << "\n"; // custom int-int map
std::cout << Config<std::map<int, char>>::get() << "\n"; // default map
}
但是,这并不能解决您希望(基于对自己的帖子的评论)在运行时指定通用类型的后备默认配置字符串的值(例如,{{ 1}})。