首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重复定义?

重复定义?
EN

Stack Overflow用户
提问于 2021-11-15 05:50:55
回答 1查看 141关注 0票数 1

我有以下代码:

代码语言:javascript
复制
#include <iostream>

/*
template <class A, std::enable_if_t<!std::is_same_v<A, double>, bool> = true>
void test() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

template <class A, std::enable_if_t<std::is_same_v<A, double>, bool> = true>
void test() {
    std::cout << "SFINAE" << std::endl;
}
*/

template <class A, typename = std::enable_if_t<!std::is_same_v<A, double>>>
void test() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

template <class A, typename = std::enable_if_t<std::is_same_v<A, double>>>
void test() {
    std::cout << "SFINAE" << std::endl;
}

int main() {
    test<int>();
    test<double>();
}

编译器抱怨

代码语言:javascript
复制
test_function_templ.cpp:21:6: error: redefinition of ‘template<class A, class> void test()’
   21 | void test() {
      |      ^~~~
test_function_templ.cpp:16:6: note: ‘template<class A, class> void test()’ previously declared here
   16 | void test() {
      |      ^~~~
test_function_templ.cpp: In function ‘int main()’:
test_function_templ.cpp:27:15: error: no matching function for call to ‘test<double>()’
   27 |  test<double>();
      |               ^
test_function_templ.cpp:16:6: note: candidate: ‘template<class A, class> void test()’
   16 | void test() {
      |      ^~~~
test_function_templ.cpp:16:6: note:   template argument deduction/substitution failed:
In file included from /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/move.h:57,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/nested_exception.h:40,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/exception:148,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/ios:39,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/ostream:38,
                 from /opt/rh/devtoolset-10/root/usr/include/c++/10/iostream:39,
                 from test_function_templ.cpp:1:
/opt/rh/devtoolset-10/root/usr/include/c++/10/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]’:
test_function_templ.cpp:15:20:   required from here
/opt/rh/devtoolset-10/root/usr/include/c++/10/type_traits:2554:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
 2554 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
      |           ^~~~~~~~~~~

如果我使用第一组函数模板(代码中注释) test(),它将按预期的方式编译和运行。

问题

  1. 对于第二组函数模板,调用test<int>()将实例化test<int, void>(),调用test<double>()将实例化test<double, void>()。但是编译器似乎看到了两个重复的模板函数?
  2. 为什么第一组函数模板没有任何问题,而第二组没有问题呢?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-11-15 06:01:48

第一个代码段的问题被描述为这里 (参见/* WRONG *//* RIGHT */代码片段分别映射到注释代码和未注释代码)。

一个常见的错误是声明两个仅在其默认模板参数中不同的函数模板。这不起作用,因为声明被视为相同函数模板的重声明(默认模板参数在功能模板等价中不考虑)。

这是我对为什么会这样的理解。

当编译器看到这个(正确的版本)

代码语言:javascript
复制
template <class A, std::enable_if_t<!std::is_same_v<A, double>, bool> = true>
void test() {}

template <class A, std::enable_if_t<std::is_same_v<A, double>, bool> = true>
void test() {}

它不会看到重声明,因为它不知道两个std::enable_if_t是否会解析为相同的类型。如果它知道,那么它将是一个硬编译时错误,就像这是一个错误:

代码语言:javascript
复制
template <class A, bool = true>
void test() {}

template <class A, bool = false> // no matter the value; signature is the same
void test() {}

这并不排除歧义可以发生在替换级别上。例如,原则上这些重载声明没有问题

代码语言:javascript
复制
template <class A, std::enable_if_t<std::is_convertible_v<A, double>, bool> = true>
void test() {}

template <class A, std::enable_if_t<std::is_convertible_v<A, int>, bool> = true>
void test() {}

但是,一旦调用test<int>(),就会出现歧义,因为编译器将能够“成功”实例化每个重载,这都会导致template<int, bool = whatever>,这使得它们变得模糊。

至于错误的版本:

代码语言:javascript
复制
template <class A, typename = std::enable_if_t<!std::is_same_v<A, double>>>
void test() {}

template <class A, typename = std::enable_if_t<std::is_same_v<A, double>>>
void test() {}

问题是,在查看如何使用它之前,编译器已经开始抱怨了,因为默认模板参数在函数模板等价中没有考虑。实际上,一个简单得多的例子显示了前面片段的问题:

代码语言:javascript
复制
template <class A, typename = typename A::foo>
void test() {}

template <class A, typename = typename A::bar>
void test() {}

注意,与前面的片段完全一样,在最后一个片段中,每个重载本身都是正确的,直到您尝试将其与特定的A一起使用时,默认参数的表达式没有意义,从而导致了一个严重的错误。

但是,如果将这两个重载放在一起,就会产生歧义,因为默认的模板参数在函数模板等效中没有考虑。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69969960

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档