繁体   English   中英

用模板向前声明所有将来的函数会导致模棱两可,而不是将声明与定义配对

[英]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 = Algorithm3SpecificFunction重载的解析(这是我们开始的问题)。因此,代码告诉编译器的是:

当且仅当(A)和(B)相同时,(A)和(B)相同。

这显然是模棱两可的。 因此,您的返回类型技巧实际上是不正确的。

问题是:为什么您首先甚至需要(A)和ReturnTypeTrick 由于模板实例化的工作方式(简而言之,专业化被懒惰地实例化,就在首次需要它们的地方之前),您根本不需要前向声明。 只需完全删除ReturnTypeTrick和(A),即可编译您的代码。

参见现场示例 另外,关于这个问题的讨论可能会很有趣。 设置与您的设置不完全相同,但是确实讨论了模板的行为与常规函数/类(与C ++标准的引用相对应,而不是此答案中所用的手法)的不同。

顺便说一句:如果您想知道为什么Algorithm1Algorithm2的专业化不存在相同的问题,那是因为非模板参数在过载分辨率方面比模板参数更好地匹配,因此(A)立即被丢弃为候选人,甚至还没有通过返回类型解析。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM