基于类型存在的模板专用化

template specialization based on a type's existance

本文关键字:专用 存在 于类型 类型      更新时间:2023-10-16

如果定义了某种类型,我想实现一些功能。问题是,如果类型不存在,编译器会将代码视为无效而拒绝。类似于基于模板的ifdef。这将在一个不知道其他代码包含什么的库中使用,如果还包括某些库,我想自动实现功能。也许这是错误的方法,但我想知道如果没有带有默认行为的ifdefs,是否可以做到这一点。

template <typename=std::enable_if<type_exists<some_type>>::type>
void function() {
   // code using some_type
}
template <>
void function() {
   // code using other_type
}

如果some_type存在,则选择第一个函数,如果some_type未定义,则选择第二个函数。该代码假定存在other_type。

另一个用例是这样的:

template <typename T, typename=std::enable_if<type_exists<boost::shared_ptr<T>>>::type>
using ptr = boost::shared_ptr<T>;
template <typename T>
using ptr = std::shared_ptr<T>;

如果库的用户包含boost,则首选boost指针,否则退回到std::shared_ptr。

如果您只想在Boost(或任何其他库)可用时才使用它,则必须使用预处理器。c++ 17附带了一个方便的__has_include宏。你可以这样使用它:

#if __has_inlcude <boost/shared_ptr.hpp>
#include <boost/shared_ptr.hpp>
template<typename T>
using ptr = boost::shared_ptr<T>;
#else
#include <memory>
template<typename T>
using ptr = std::shared_ptr<T>;
#end

您也可以使用#if defined(BOOST_VERSION)来支持旧的编译器,但这依赖于已经包含的特定boost头,并且是脆弱的(您冒着ptr指向翻译单元之间不同的东西的风险)


以下是一些标准(来自N4140):

§3.4.3 (basic.lookup.qual)/1:

可以引用类或命名空间成员或枚举数的名称在::范围解析运算符应用于a之后嵌套名称说明符,表示其类、名称空间或枚举。如果一个::范围解析运算符在嵌套名称说明符之前没有类型说明符,查找::只考虑名称空间、类型和特化为类型的模板。如果找到的名称不是指定名称空间或类、枚举或依赖类型

不幸的是,我认为在编译时没有简单的方法来确定类型是否定义,主要是由于c++预处理器的愚蠢。

此外,__has_include不在当前的c++标准与,依靠#if __has_include (<boost/shared_ptr.hpp>)不能解决你的问题,因为你不能说如果<boost/shared_ptr.hpp>包含你的代码,那么你的编译器没有std::shared_ptr

3可能的解决方案:

  1. 如果您想使用boost处理较新的标准库,请考虑使用<boost/config.hpp>。例如,BOOST_NO_CXX11_SMART_PTR宏将帮助确定std库是否有shared_ptr

  2. 你可以实现自己的宏来检查编译器是否支持某些库,通过检查不同的编译器版本。然而,由于编译器的选项,这并不是一件容易的事。(例如gcc的-std=c++03 vs -std=c++11)

  3. 你的工具链可能可以检查你的编译器的特性,并通过宏传递给编译器。看看是否配置。对于autoconf, FindSharedPtr。