根据类型特征专门化 C++ 11 模板,同时允许回退其他类型

Specialize C++11 template based on type traits while allowing fallback for other types

提问人:kmhaug 提问时间:9/26/2023 更新时间:9/28/2023 访问量:33

问:

我目前正在将几个数据结构序列化为 JSON。

现有的数据结构已经提供了一种基于文本的输出格式,当将特定类型转换为 JSON 的函数尚不存在时,我想将其用作默认格式。

我目前已经完成了回退和几个特定的专业化。由于我使用的 JSON 库已经能够使用像 int、float 等素数,而无需任何额外的工作,我希望这些类型有自己的(通用)专用模板函数。

我目前的主要问题是我无法调用基本原理/原语的函数,但是如果我手动指定类型(),则会调用正确的函数。有什么办法可以解决这个问题吗?编译器在第一行中推断出什么类型?to_json(1)to_json<int>

我仅限于 C++11。

下面总结了我的基本设置


#include <iostream>
#include <type_traits>

// Should rely on fallback
struct S {
};

// Has specialization
struct R {
};


// Fallback. Should catch everything not caught by the specializations
template <typename T>
void to_json(const T& x);


// Specialization for R
template<>
void to_json(const R& x)
{
    std::cout << "For R\n";
}

// Does not work
// Specialization for all fundamentals (e.g. int)
template<typename T>
void to_json(const typename std::enable_if<std::is_fundamental<T>::value, T>::type& x)
{
    std::cout << "For fundamentals\n";
}

template <typename T>
void to_json(const T& x)
{
    std::cout << "General\n";
}

int main()
{
    to_json(1);      // General. (WRONG, expected fundamentals)
    to_json<int>(1); // fundamentals (CORRECT)
    to_json(R{});    // R (CORRECT)
    to_json(S{});    // General (CORRECT)

    return 0;
}

输出:

General
For fundamentals
For R
General
C++11 模板 重载 回退 类型推导

评论

1赞 Igor Tandetnik 9/28/2023
没有函数模板的部分专用化。您的第二个模板是重载,而不是专业化。另外,您定义其参数的方式是不可推导的上下文:无法从参数中推导出来。将 SFINAE 放在返回类型上,而不是参数类型上;虽然我怀疑你会有模棱两可的超载。to_jsonT
1赞 Igor Tandetnik 9/28/2023
也许是类似的东西,使用实际上可以部分专用的帮助程序类。
0赞 Klaus 9/28/2023
你真的需要C++11吗?我们有 2023 年,使用 12 年过时的标准看起来有点神秘。使用具有可用“概念”的最新编译器可以大大简化此类任务。可读且易于维护的代码不仅有助于今天...
0赞 kmhaug 9/29/2023
谢谢你的澄清,伊戈尔。帮助程序类的好方法,似乎是一个很好的解决方案。话虽如此,billz的解决方案(适用于C++11)似乎更简单一些。至于标准的选择,Kaus恐怕超出了我的控制范围,不幸的是。

答:

1赞 billz 9/28/2023 #1

您的正向声明模板函数将捕获所有类型。当用混凝土类型初始化时,它会带来麻烦。

//// Fallback. Should catch everything not caught by the specializations
//template <typename T>
//void to_json(const T& x);

下面的代码应该可以正常工作:

// Should rely on fallback
struct S {
};

// Has specialization
struct R {
};

// note: also use enable_if_t to exclude fundamental type
//       otherwise compiler is still confused by the redefinition
template <typename T,
        std::enable_if_t<!std::is_fundamental<T>::value, bool> = true>
void to_json(const T& x)
{
    std::cout << "General\n";
}
    
// Specialization for R
template<>
void to_json(const R& x)
{
    std::cout << "For R\n";
}

// Specialization for all fundamentals (e.g. int)
template<typename T,
         std::enable_if_t<std::is_fundamental<T>::value, bool> = true>
void to_json(const T& x)
{
    std::cout << "For fundamentals\n";
}

int main()
{
    to_json(1);      // General. (WRONG, expected fundamentals)
    to_json<int>(1); // fundamentals (CORRECT)
    to_json(R{});    // R (CORRECT)
    to_json(S{});    // General (CORRECT)

    return 0;
}

评论

0赞 kmhaug 9/29/2023
谢谢你的解决方案!但是,它不使用 C++11(即 C++14)进行编译。如果我们写例如.如果此重写有任何(已知的)不良副作用,请告诉我。enable_if_tstd::enable_if<std::is_fundamental<T>::value>::type* = nullptr
0赞 kmhaug 10/2/2023
更正:可能是一个更好的解决方案typename std::enable_if<std::is_fundamental<T>::value, bool>::type=true