使用模板专业化,我编写了一系列具有相同名称和相同参数类型的函数,但是返回了由template参数指定的类型的数据:
template<typename T> T f (int x); // Purposefully unimplemented.
template<> inline uint8_t f<uint8_t> (int x) { return f8 [x]; }
template<> inline uint16_t f<uint16_t>(int x) { return f16 [x]; }
template<> inline uint32_t f<uint32_t>(int x) { return f32 [x]; }
template<> inline uint64_t f<uint64_t>(int x) { return f64 [x]; }
然后我可以编写如下代码:
uint32_t a = f<uint32_t>(3);
uint64_t b = f<uint64_t>(7);
如果有人尝试将f版本用于除我为其定义的专用类型之外的其他任何内容,则我故意不使用默认模板,以产生链接器错误。
我有两个问题:
1)如果有人尝试使用比我现在所获得的更为友好的默认模板,是否可以通过某种方式使用static_assert()
(或任何其他方式)来产生编译错误(而不是链接器错误): 对“ int f(int)”的未定义引用?
2)是否有某种方法可以使用模板来维持与程序员相同的接口,但是不需要模板专门化? (即,是否有某种方法可以完全避免使用默认模板?)
答案 0 :(得分:2)
namespace fimpl{
template<class T>struct tag_t{};
template<class T>
void fimpl(tag_t<T>, int x)=delete;
}
template<typename T> T f (int x){ using fimpl::fimpl; return fimpl(fimpl::tag_t<T>{}, x); }
现在不专攻;覆盖。
namespace fimpl{ inline uint8_t fimpl(tag_t<uint8_t>, int x) { return f8 [x]; } }
namespace fimpl{ inline uint16_t fimpl(tag_t<uint16_t>, int x) { return f16 [x]; } }
namespace fimpl{ inline uint32_t fimpl(tag_t<uint32_t>, int x) { return f32 [x]; } }
namespace fimpl{ inline uint64_t fimpl(tag_t<uint64_t>, int x) { return f64 [x]; } }
这使用标签分派来选择哪个替代,而不是使用专门化。
如果未找到明确的专业化名称,则会选择=delete
模板,并立即获得编译器错误。
有趣的是,如果您想使用新的类型扩展它,例如说namespace lib{ struct bigint; }
,则可以在fimpl(fimpl::tag_t<bigint>, int)
中放置namespace lib
重载,这样就可以了。我怀疑您会需要它。
如果您对f
而不是f(tag<uint8_t>, 7)
感到满意,那么也可以取消使用f<uint8_t>(7)
作为模板。只需摆脱fimpl
名称空间(将内容移出),将fimpl::fimpl
重命名为f
,删除=delete
ed模板函数,添加template<class T> constexpr tag_t<T> tag{};
。但是在调用时语法有点不同。
答案 1 :(得分:1)
1)如果有人尝试使用比我现在所得到的更友好的默认模板,有什么方法可以使用static_assert(或其他方法)产生编译错误(而不是链接器错误)? int f(int)'?
我认为更好的解决方案是Passer By在评论中建议的解决方案:
template<typename T> T f (int x) = delete;
但是如果您真的想使用static_assert()
...我想您可以尝试以下操作
template<typename T>
T f (int x)
{
static_assert( sizeof(T) == std::size_t(-1), "f()!" );
return {};
}
2)是否有某种方法可以使用模板来维持与程序员相同的接口,但是不需要模板专门化? (即,是否有某种方法可以完全避免使用默认模板?)
我不清楚我到底想要什么。
您不想要专业化,并且想要避免使用默认模板吗?
假设您只需要仅适用于特定类型集合的默认模板,我想您可以使用SFINAE。
举个例子,只有f()
是整数类型时,才启用以下T
。
template<typename T>
typename std::enable_if<std::is_integral<T>{}, T>::type f (int x)
{ return x; }
以下是完整的编译示例
#include <iostream>
#include <type_traits>
template<typename T>
typename std::enable_if<std::is_integral<T>{}, T>::type f (int x)
{ return x; }
int main ()
{
auto f16 = f<std::uint16_t>(0);
auto f32 = f<std::uint32_t>(0);
static_assert( std::is_same<decltype(f16), std::uint16_t>{}, "!" );
static_assert( std::is_same<decltype(f32), std::uint32_t>{}, "!" );
// compilation error
// auto fd = f<double>(0);
}