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

bgibtngc  于 2023-01-06  发布在  其他
关注(0)|答案(2)|浏览(160)

使用模板专门化,我编写了一系列函数,它们都具有相同的名称和相同的参数类型,但返回由模板参数指定的类型的数据:

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)有没有什么方法可以让模板保持与程序员相同的接口,但不需要模板专门化?(也就是说,有没有什么方法可以完全避免默认模板?)

n53p2ov0

n53p2ov01#

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; },你可以在namespace lib中放置一个fimpl(fimpl::tag_t<bigint>, int)重载,它会工作。
如果你同意用f(tag<uint8_t>, 7)代替f<uint8_t>(7),你也可以不用f作为模板。只要去掉fimpl命名空间(把东西移走),把fimpl::fimpl重命名为f,去掉=delete ed模板函数,加上template<class T> constexpr tag_t<T> tag{};。但是在调用点语法有点不同。

c90pui9n

c90pui9n2#

1.如果有人试图使用比我现在得到的模板更友好的默认模板,我是否可以使用static_assert(或其他方法)来产生编译错误(而不是链接器错误):未定义对'int f(int)'的引用?
我认为更好的解决方案是路人在评论中建议的:

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

1.有没有什么方法可以让模板保持与程序员相同的接口,但不需要模板专门化?(也就是说,有没有什么方法可以完全避免默认模板?)
我不清楚你到底想要什么。
你不想专门化,你想避免默认模板?
假设您只需要只适用于特定类型集的默认模板,我想您可以使用SFINAE。
举个例子,只有当T是整数类型时,才启用下面的f()

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

相关问题