如果使用默认模板,如何产生编译器错误?

时间:2018-08-09 11:24:46

标签: c++ c++11 templates template-specialization static-assert

使用模板专业化,我编写了一系列具有相同名称和相同参数类型的函数,但是返回了由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)是否有某种方法可以使用模板来维持与程序员相同的接口,但是不需要模板专门化? (即,是否有某种方法可以完全避免使用默认模板?)

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);
 }