函数模板的 C++ Decltype

moj*_*jo2 5 c++ templates

我想创建一个重载模板,如果一个类包含它,它会运行一个函数 Foo() ,否则它什么都不做。

class A
{
public:
    template <typename U>
    void Foo(U& u)
    {
        std::cout << "Has Foo()" << std::endl;
        // Modify u
    }
};

class B
{
    // Does not contain Foo()
};
Run Code Online (Sandbox Code Playgroud)

我一直在尝试运行它

template <typename T, typename U>
decltype(std::declval<T>().Foo()) TriggerFoo(T* t, U& u)
{
    t->Foo(u);
}
template <typename T, typename U>
void TriggerFoo(T* t, U& u)
{
    std::cout << "Does not have Foo()" << std::endl;
}

int main()
{
    A a;
    B b;
    U u;     // Some type

    TriggerFoo<A, U>(&a, u);    // I want to print "Has Foo()".
    TriggerFoo<B, U>(&b, u);    // Prints "Does not have Foo()".

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

目前,这两个类都被传递给“没有 Foo()”实例化。它可以编译,但显然它不起作用,这很可能是因为我不太了解 declval。我也尝试过非模板函数,但它仍然不起作用。

任何帮助将不胜感激。

小智 5

扩展 Sam 的答案,如果您不使用指针,您可以进一步简化代码,使其看起来更整洁。

#include <iostream>
#include <type_traits>

class A
{
public:
    template <typename U>
    void Foo(U& u)
    {
        std::cout << "Has Foo()\n";
    }
};

class B
{
    // Does not contain Foo()
};

template <
  typename T,
  typename U,
  typename Z=decltype(std::declval<T>().Foo(std::declval<U&>()))>
void TriggerFoo(T& t, U& u)
{
    t.Foo(u);
}

template <typename... T>
void TriggerFoo(const T&...)
{
    std::cout << "Does not have Foo()\n";
}

class U {};

int main()
{
    A a;
    B b;
    U u;

    TriggerFoo<A, U>(a, u);    // I want to print "Has Foo()".
    TriggerFoo<B, U>(b, u);    // Prints "Does not have Foo()".

    return 0;
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*hik 4

您的方法存在两个基本问题:

decltype(std::declval<T>().Foo())
Run Code Online (Sandbox Code Playgroud)

这永远不会成功解决,因为Foo()有问题的总是带有一个参数。这部分应该是:

decltype(std::declval<T>().Foo(std::declval<U &>()))
Run Code Online (Sandbox Code Playgroud)

但现在你会遇到一个不同的问题:当类实现 时Foo(),模板解析将变得不明确。可以使用任一模板函数。

因此,您需要另一级间接,以及不同优先级的模板:

#include <iostream>
#include <type_traits>

class A
{
public:
    template <typename U>
    void Foo(U& u)
    {
        std::cout << "Has Foo()" << std::endl;
        // Modify u
    }
};

class B
{
    // Does not contain Foo()
};

template <typename T, typename U, typename Z=decltype(std::declval<T>().Foo(std::declval<U &>()))>
void DoTriggerFoo(T* t, U& u, int dummy)
{
    t->Foo(u);
}

template <typename T, typename U>
void DoTriggerFoo(T* t, U& u, ...)
{
    std::cout << "Does not have Foo()" << std::endl;
}

template <typename T, typename U>
void TriggerFoo(T *t, U &u)
{
    DoTriggerFoo(t, u, 0);
}

class U {};

int main()
{
    A a;
    B b;
    U u;     // Some type

    TriggerFoo<A, U>(&a, u);    // I want to print "Has Foo()".
    TriggerFoo<B, U>(&b, u);    // Prints "Does not have Foo()".

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

gcc 5.3 的结果:

$ ./t
Has Foo()
Does not have Foo()
Run Code Online (Sandbox Code Playgroud)

附:

std::declval<T &>().Foo(std::declval<U &>())
Run Code Online (Sandbox Code Playgroud)

对于您的实际课程来说,这可能会效果更好。

  • 它不仅仅是虚拟变量本身,还包括其他模板函数中相应的“...”参数占位符。包装器为相应的参数传递 0。当 decltype 解析成功时,两个模板函数都会解析,但带有“...”参数占位符的函数的优先级低于声明显式“int”参数的函数。这解决了歧义。 (2认同)