启用非模板成员函数,前提是它会进行类型检查

问题描述 投票:0回答:1

例如

template <class T1, class T2>
class foo
{
    T1 t1;
    T2 t2;

    T1 bar(); //Always exists
    decltype(t1(t2)) baz(); //Should only exist if t1(t2) is valid
};

如果

baz
无效,我仍然希望程序能够编译,只要没有人真正调用
baz

c++ c++11 sfinae c++-templates
1个回答
4
投票

您可以将

baz
制作为模板,这样,如果返回类型无效,则该成员将被 SFINAE-d 淘汰,而不是导致硬错误:

template <class T1, class T2>
class foo
{
    T1 t1;
    T2 t2;

    T1 bar(); //Always exists

    template<typename T = T1>
    decltype(std::declval<T&>()(t2)) baz();
};

T
参数对于使计算表达式依赖于类型是必需的,否则 SFINAE 不适用。如果您担心此实现细节“泄漏”并且有人可能会尝试
f.baz<int>()
,您可以使用
baz
声明
template<typename... Dummy, typename T = T1>
并强制执行正确的使用,例如函数体中的
static_assert( sizeof...(Dummy) == 0, "Incorrect usage" );
。这两种方法确实使获取成员的地址变得更加困难:它应该看起来像例如:
&foo<T, U>::baz<>

另一种方法是引入类模板专门化:

template<typename...> struct void_ { using type = void; };

template<typename T1, typename T2, typename = void>
class foo {
    // version without baz
};

template<typename T1, typename T2>
class foo<T1, T2, typename void_<decltype(std::declval<T1&>()(std::declval<T2>()))>::type> {
    decltype(std::declval<T1&>()(std::declval<T2>())) baz();
};

在这种情况下,

&foo<T, U>::baz
可以获取会员的地址(当然假设它存在)。两种专业化所共有的代码可以在一个公共基础中分解出来,如果担心作为实现细节引入的附加模板参数可能会泄漏,则可以有一个“真正的”
foo
,只需要两个模板参数依次继承自这样的实现。

© www.soinside.com 2019 - 2024. All rights reserved.