成员函数中的decltype(auto)忽略无效的body,decltype(expr)失败

时间:2015-04-06 00:10:05

标签: c++ templates c++11 c++14

我有一个简单的模板化包装结构,其成员函数在其模板类型的对象上调用.error()

template <typename T>
struct Wrapper {
    T t;
    decltype(auto) f() {
        return t.error(); // calls .error()
    }
};

如果我使用不具有error()成员函数的类型对其进行实例化,只要我不调用它就没问题。这是我想要的行为。

Wrapper<int> w; // no problem here
// w.error(); // uncommented causes compilation failure

如果我使用我认为是具有尾随返回类型的语义等价物,则它在变量声明上出错

template <typename T>
struct Wrapper {
    T t;
    auto f() -> decltype(t.error()) {
        return t.error();
    }
};

Wrapper<int> w; // error here

我接受这两者在语义上并不相同,但无论如何都要使用尾随返回类型(仅限C ++ 11)来获取前者的行为,而不使用某种{专用整个类{1}} tmp trickery?

2 个答案:

答案 0 :(得分:5)

版本之间的差异

decltype(auto) f();
auto f() -> decltype(t.error());

是第二个函数声明可能无效。当定义实例化 [dcl.spec.auto] / 12 时,会发生函数模板的返回类型推导。虽然我找不到关于类模板的成员函数的返回类型推导的任何内容,但我认为它们的行为类似。

隐式实例化类模板Wrapper导致声明的实例化,而不是所有(非虚拟)成员函数的 definitions 的实例化< SUP> [temp.inst] / 1 。声明decltype(auto) f();有一个未推断的占位符但有效。另一方面,auto f() -> decltype(t.error());对于某些实例化具有无效的返回类型。


C ++ 11中的一个简单解决方案是推迟确定返回类型,例如:将f转换为函数模板:

template<typename U = T>
auto f() -> decltype( std::declval<U&>().error() );

然而,该功能的定义让我有点担心:

template<typename U = T>
auto f() -> decltype( std::declval<U&>().error() )
{
    return t.error();
}

对于其中Wrapper无效的t.error()的专精,上述f是一个无法生成有效专业化的函数模板。这可能属于[temp.res] / 8,这表示此类模板格式错误,无需诊断:

  

如果无法为模板生成有效的专业化,并且未实例化该模板,则模板格式错误,无法诊断   必需的。

但是,我怀疑该规则已经引入 allow ,但并不要求实现检查非实例化模板中的错误。在这种情况下,源代码中没有编程错误;在源代码描述的类模板的实例化中会发生错误。因此,我认为应该没问题。


另一种解决方案是使用回退返回类型使函数声明格式正确,即使定义不是(对于所有实例化):< / p>

#include <type_traits>

template<typename T> struct type_is { using type = T; };

template <typename T>
struct Wrapper {
    T t;

    template<typename U=T, typename=void>
    struct error_return_type_or_void : type_is<void> {};

    template<typename U>
    struct error_return_type_or_void
        <U, decltype(std::declval<U&>().error(), void())>
    : type_is<decltype(std::declval<U&>().error())> {};

    auto f() -> typename error_return_type_or_void<>::type {
        return t.error();
    }
};

答案 1 :(得分:2)

一种方法是标记的crtp。

  // todo:
  template<class T>
  struct has_error; // true_type if T.error() is valid

  template<class D,class T,bool Test=has_error<T>{}>
  struct do_whatever {
    D* self(){return static_cast<D*>(this);}
    D const* self()const{return static_cast<D const*>(this);}

    auto f()->decltype(self()->t.error()) {
      return self()->t.error();
    }
  };
  template<class D,class T>
  struct do_whatever<D,T,false>{};
如果f()没有T

现在error()就不存在了。