c++ 模板函数的特殊化调用与原型不明确

7d7tgy0s  于 2023-03-25  发布在  其他
关注(0)|答案(1)|浏览(138)

我刚刚为序列化或(一些)对象和一些原生类型编写了函数,遇到了一个奇怪的错误:

<source>: In function 'int main()':
<source>:29:34: error: call of overloaded 'deserialize<long long int>(const char [5])' is ambiguous
   29 |     deserialize<long long>("1337");
      |                                  ^
<source>:15:3: note: candidate: 'T deserialize(const string&) [with T = long long int; std::string = std::__cxx11::basic_string<char>]'
   15 | T deserialize(std::string const &);
      |   ^~~~~~~~~~~
<source>:26:11: note: candidate: 'typename std::enable_if<(((! is_specialization<T, std::vector>::value) && (! is_specialization<T, std::map>::value)) && (! is_specialization<T, std::tuple>::value)), T>::type deserialize(const string&) [with T = long long int; typename std::enable_if<(((! is_specialization<T, std::vector>::value) && (! is_specialization<T, std::map>::value)) && (! is_specialization<T, std::tuple>::value)), T>::type = long long int; std::string = std::__cxx11::basic_string<char>]'
   26 | long long deserialize<long long>(std::string const & llstr) { return {}; }
      |           ^~~~~~~~~~~~~~~~~~~~~~

我有一个原型,一个重载和一个像这样的特殊化:

#include <string>
#include <type_traits>
#include <vector>
#include <map>
#include <tuple>

template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};

// Prototype
template <typename T>
T deserialize(std::string const &);

// Default Implementation (Overload)
template <typename T>
typename std::enable_if<!is_specialization<T, std::vector>::value &&
                        !is_specialization<T, std::map>::value &&
                        !is_specialization<T, std::tuple>::value, T>::type
deserialize(std::string const &) { return {}; }

// Specialization
template <>
long long deserialize<long long>(std::string const & llstr) { return {}; }

int main() {
    deserialize<long long>("1337");
}

如果我调用特殊化,就像在错误消息的例子中一样,我得到上面的错误。但是为什么呢?特殊化不应该隐藏重载吗?为什么编译器在显示错误时指向原型?
我想在我的hpp文件中有一个原型,但只有当我删除原型时,这个代码才能工作。我怎么能同时拥有这两个呢?
我提前感谢你的帮助!

编辑:

很抱歉没有提供is_specialization463035818-is-not-a-number的实现。我是从here得到的。
至于我提到的头文件的原型,Remy Lebeau:我喜欢有两个hpp和一个cpp文件:在(主)hpp(对于我想实现的类,我只放声明,没有定义。在“…_imp.hpp”(包含在另一个hpp的末尾)中,我放了函数模板的定义。剩下的(非模板定义和模板函数的完整规范)我在cpp文件中编写。为了测试,我使用在线ide,所以它都是同一个“文件”的一部分,但是对于真实的的项目,我想以我刚才提到的方式分割代码,我觉得有必要给出为什么我想要一个单独的原型的理由。
是的,对于deserialize-函数来说,最有意义的是通过返回类型来区分,因为这是不可能的,我认为模板是最好的方法。谢谢你的建议,Nathan Oliver。我会研究一下,看看标签调度是否有帮助!

hfsqlsce

hfsqlsce1#

特殊化可以应用于这两个重载。编译器使用最后一个声明的重载。因此另一个重载仍然没有特殊化。
如果将专门化的顺序与第二个重载交换,就可以很好地看到这一点。
最简单的解决方案是使用if constexpr

#include <string>
#include <type_traits>
#include <vector>
#include <map>
#include <tuple>

template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};

template <typename T>
T deserialize(std::string const &) {
    if constexpr (
        !is_specialization<T, std::vector>::value &&
        !is_specialization<T, std::map>::value &&
        !is_specialization<T, std::tuple>::value
    ) {
      return {};
    } else if constexpr(std::is_same_v<T, long long>){
      return {};
    } else {
      return {};
    }
}

int main() {
    deserialize<long long>("1337");
}

相关问题