3

我有一个像这样的函数定义

template <typename T>
auto print(T t) -> decltype(t.print()) {
    return t.print();
}

这个想法是参数必须是类型T并且必须具有print函数。这个print函数可以返回任何东西,解释decltype. 因此,例如,您可以这样做:

struct Foo
{
    int print()
    {
        return 42;
    }
};

struct Bar
{
    std::string print()
    {
        return "The answer...";
    }
};

...

std::cout << print(Foo()) << std::endl;    
std::cout << print(Bar()) << std::endl;
/* outputs: 
42
The answer...
*/

我读到模板不能进行运行时实例化,并且您可以让类从基类派生,然后确定它们的类型以查看要使用的模板参数。但是,我将如何为一种non-class类型执行此操作?这个想法是能够拥有:

template <typename T>
T print(T t) {
    return t;
}

同样,但这给了我模棱两可的过载错误。排位赛不起作用,即print<Foo>. 另一个问题是,如果我有一个仿函数怎么办:

struct Foo
{
  virtual int print();
  operator int() const
  {
    return 42;
  }
};

它现在如何决定?

所以我的问题是,是否有可能用模板解决所有这些歧义,还是我必须编写一堆冗余代码?

测试

我逐步添加测试,从下面复制/粘贴每个编辑过的解决方案。结果如下:

有以下课程:

struct Foo
{
    int print()
    {
        return 42;
    }

    operator int() const
    {
        return 32;
    }
};

struct Bar
{
    std::string print()
    {
        return "The answer...";
    }

    operator int() const
    {
        return (int)Foo();
    }
};

struct Baz
{
    operator std::string() const
    {
        return std::string("The answer...");
    }
};

以及以下测试输出:

std::cout << print(Foo()) << std::endl;    
std::cout << print(Bar()) << std::endl;
std::cout << print(42) << std::endl;
std::cout << print((int)Foo()) << std::endl;
std::cout << print("The answer...") << std::endl;
std::cout << print(std::string("The answer...")) << std::endl;
std::cout << print((int)Bar()) << std::endl;
std::cout << print((std::string)Baz()) << std::endl;

两者都正确输出:

42
The answer...
42
32
The answer...
The answer...
32
The answer...
4

2 回答 2

5

您可以采用以下方法,print()如果存在这样的成员函数,则在输入上调用成员函数,否则它将返回输入本身:

namespace detail
{
    template<typename T, typename = void>
    struct print_helper
    {
        static T print(T t) {
            return t;
        }
    };

    template<typename T>
    struct print_helper<T, decltype(std::declval<T>().print(), (void)0)>
    {
        static auto print(T t) -> decltype(t.print()) {
            return t.print();
        }
    };
}

template<typename T>
auto print(T t) -> decltype(detail::print_helper<T>::print(t))
{
    return detail::print_helper<T>::print(t);
}

这是一个活生生的例子

于 2013-05-12T08:53:01.827 回答
2

对要直接打印的每种类型使用手动重载的简单解决方案:

定义你的第一个实现,它调用T::print(). 使用重载为所有没有此功能的类型指定替代实现。我不推荐这种解决方案,但它很容易理解。

template<typename T>
auto print(T t) -> decltype(t.print()) {
    return t.print();
}

int print(int t) {
    return t;
}
std::string print(std::string t) {
    return t;
}
// ... and so on, for each type you want to support ...

使用SFINAE的更高级的解决方案,T::print()当且仅当它存在时自动使用:

首先,定义一个特征,它可以决定你的类型是否有一个 function print()。基本上,此特征继承自std::true_typeor std::false_type,具体取决于在某个帮助器类 ( _test_print) 中做出的决定。enable_if然后,在编译时决策中使用此类型特征,该决策定义两种情况中的一种并隐藏另一种情况(因此这不是重载)。

// Type trait "has_print" which checks if T::print() is available:
struct _test_print {
    template<class T> static auto test(T* p) -> decltype(p->print(), std::true_type());
    template<class>   static auto test(...)  -> std::false_type;
};
template<class T> struct has_print : public decltype(_test_print::test<T>(0)) {};


// Definition of print(T) if T has T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<has_print<T>::value, decltype(t.print())>::type {
    return t.print();
}

// Definition of print(T) if T doesn't have T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<!has_print<T>::value, T>::type {
    return t;
}

看看现场演示

于 2013-05-12T08:57:02.250 回答