[英]Forward declaring all future functions with a template results in ambiguity instead of pairing declaration with definition
我正在使用一个c ++惯用法,该惯用法允许定义一个通用行为( GeneralFunction
),该行为通过使其调用特定函数( SpecificFunction
)进行自定义-通过标记类在它们之间切换(不幸的是,我忘记了该惯用语的名称)。
我决定要让SpecificFunctions具有任意返回类型。 但是由于我们使用的是c ++,因此我需要在GeneralFunction
之前先声明标识符。
我希望在这里声明一个SpecificFunction模板,该模板在实例化时将声明一个与具体的SpecificFunction重载匹配的声明,该重载将在下面进行定义:
#include <utility>
#include <iostream>
template <typename Tag, typename ... Args>
struct ReturnTypeTrick;
template <typename Tag, typename ... Args>
inline auto SpecificFunction(Tag, Args&& ... args) -> typename ReturnTypeTrick<Tag, Args...>::type; // basically saying SpecificFunction returns what it returns
返回类型在实例化之前是未知的,但是现在不重要了。 我只需要声明标识符,以便可以在GeneralFunction
使用它
template <typename Tag, typename ... Args>
struct ReturnTypeTrick {
using type = decltype(SpecificFunction(Tag(), std::declval<Args>() ...));
};
具有常见行为的一般功能:
template <typename Tag, typename ... Args>
inline auto GeneralFunction (Args&& ... specific_args) {
// do something common for all implementations
// this is where the behavior differs
return SpecificFunction(Tag(), std::forward<Args>(specific_args) ...);
}
我决定提供的SpecificFunction的默认实现:
template <typename Tag, typename ... Args>
inline bool SpecificFunction(Tag, Args&& ...) { // default implementation
return false;
}
定义特定的行为功能:
struct Algorithm1 {};
inline auto SpecificFunction(Algorithm1, int param1, char param2) {
return 10;
}
struct Algorithm2 {};
inline auto SpecificFunction(Algorithm2, long param1) {
return "y";
}
struct Algorithm3 {};
int main() {
std::cout << GeneralFunction<Algorithm1>(1, 'a') << std::endl;
std::cout << GeneralFunction<Algorithm2>(1l) << std::endl;
/* these somehow work, probably independently on the
`SpecificFunction` declaration above because my compiler is OK with
functions not being declared at all. BUT changing these simple
SpecificFunction definitions into templates – that is line in
GeneralFunction would look like
return SpecificFunction<SomeTemplateArg>(Tag(), std::forward<Args>(specific_args) ...);
– and removing the declaration results in undeclared indentifier
`SpecificFunction`, so I need to be able to declare it */
std::cout << GeneralFunction<Algorithm3>("wtf this should call the // default implementation") << std::endl;
// error: call to 'SpecificFunction' is ambiguous
static_assert(std::is_same_v<typename ReturnTypeTrick<Algorithm3, const char*>::type, bool>, "the return type of the generated declaration is correct if you don't see this message");
return 0;
}
错误信息:
/scratch_2.cpp:22:12: error: call to 'SpecificFunction' is ambiguous
return SpecificFunction(Tag(), std::forward<Args>(specific_args) ...);
^~~~~~~~~~~~~~~~
/scratch_2.cpp:51:18: note: in instantiation of function template
specialization 'GeneralFunction<Algorithm3, char const (&)[52]>' requested here
std::cout << GeneralFunction<Algorithm3>("wtf this should call the // default implementation") << std::endl;
^
/scratch_2.cpp:8:13: note: candidate function [with Tag = Algorithm3, Args
= <char const (&)[52]>]
inline auto SpecificFunction(Tag, Args&& ... args) -> typename ReturnTypeTrick<Tag, Args...>::type; // basically saying Spe...
^
/scratch_2.cpp:26:7: note: candidate function [with Tag = Algorithm3, Args
= <char const (&)[52]>]
inline bool SpecificFunction(Tag, Args&& ...) { // default implementation
似乎无法将生成的声明与默认的SpecificFunction
实现的定义匹配。
您可以看到生成的声明与定义头相同(他们称其错误:不明确)。 这很有趣,因为那正是身份(我认为声明和定义已配对)的基础。
我也尝试将// default implementation
SpecificFunction的标头更改为
template <typename Tag>
inline bool SpecificFunction(Tag, ...) { // default implementation
return false;
}
但是然后我得到一个链接器错误:
Undefined symbols for architecture x86_64:
"ReturnTypeTrick<Algorithm3, char const (&) [52]>::type SpecificFunction<Algorithm3, char const (&) [52]>(Algorithm3, char const (&&&) [52])", referenced from:
auto GeneralFunction<Algorithm3, char const (&) [52]>(char const (&&&) [52]) in scratch_2-b3bb4f.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
首先请注意,返回类型是功能模板专业化功能签名的一部分。
因此,编译器会看到两个与Tag = Alogirthm3
匹配的重载,它们的返回类型可能不同:
// (A)
template <typename Tag, typename ... Args>
inline auto SpecificFunction(Tag, Args&& ... args)
-> typename ReturnTypeTrick<Tag, Args...>::type;
和
// (B)
template <typename Tag, typename ... Args>
inline bool SpecificFunction(Tag, Args&& ...);
要弄清楚它们实际上是相同的,它必须弄清楚什么是typename ReturnTypeTrick<Tag, Args...>::type
,其定义为decltype(SpecificFunction(Tag(), std::declval<Args>() ...)
,再次导致Tag = Algorithm3
的SpecificFunction
重载的解析(这是我们开始的问题)。因此,代码告诉编译器的是:
当且仅当(A)和(B)相同时,(A)和(B)相同。
这显然是模棱两可的。 因此,您的返回类型技巧实际上是不正确的。
问题是:为什么您首先甚至需要(A)和ReturnTypeTrick
? 由于模板实例化的工作方式(简而言之,专业化被懒惰地实例化,就在首次需要它们的地方之前),您根本不需要前向声明。 只需完全删除ReturnTypeTrick
和(A),即可编译您的代码。
参见现场示例 。 另外,关于这个问题的讨论可能会很有趣。 设置与您的设置不完全相同,但是确实讨论了模板的行为与常规函数/类(与C ++标准的引用相对应,而不是此答案中所用的手法)的不同。
顺便说一句:如果您想知道为什么Algorithm1
和Algorithm2
的专业化不存在相同的问题,那是因为非模板参数在过载分辨率方面比模板参数更好地匹配,因此(A)立即被丢弃为候选人,甚至还没有通过返回类型解析。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.