简体   繁体   English

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

[英]decltype(auto) in member function ignores invalid body, decltype(expr) fails

I have a simple templated wrapper struct with a member function calling .error() on an object of its template type. 我有一个简单的模板化包装器结构,其成员函数在其模板类型的对象上调用.error()

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

If I instantiate this with a type that doesn't have an error() member function, it's fine as long as I don't call it. 如果我使用没有error()成员函数的类型实例化它,只要我不调用它就没问题。 This is the behavior I want. 这是我想要的行为。

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

If I use what I thought was the semantic equivalent with a trailing return type, it errors on the variable declaration 如果我使用我认为是跟踪返回类型的语义等价物,它就会在变量声明中出错

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

Wrapper<int> w; // error here

I'll accept that the two are not semantically equivalent, but is there anyway to get the behavior of the former using a trailing return type (C++11 only) without specializing the whole class with some kind of HasError tmp trickery? 我会接受两者在语义上没有等价,但是无论如何使用尾随返回类型(仅限C ++ 11)来获取前者的行为而不使用某种HasError tmp HasError专门化整个类?

The difference between the versions 版本之间的差异

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

is that the function declaration of the second can be invalid. 是第二个函数声明可能无效。 Return type deduction for function templates happens when the definition is instantiated [dcl.spec.auto]/12 . 函数模板的返回类型推导在定义实例化时发生[dcl.spec.auto] / 12 Although I could not find anything about return type deduction for member functions of class templates, I think they behave similarly. 虽然我找不到关于类模板的成员函数的返回类型推导的任何内容,但我认为它们的行为类似。

Implicitly instantiating the class template Wrapper leads to the instantiation of the declarations , but not of the definitions of all (non-virtual) member functions [temp.inst]/1 . 隐式地实例化类模板Wrapper导致声明的实例,但不是所有的(非虚拟)成员函数[temp.inst] / 1定义 The declaration decltype(auto) f(); 声明decltype(auto) f(); has a non-deduced placeholder but is valid. 有一个未推断的占位符但有效。 On the other hand, auto f() -> decltype(t.error()); 另一方面, auto f() -> decltype(t.error()); has an invalid return type for some instantiations. 某些实例化的返回类型无效。


A simple solution in C++11 is to postpone the determination of the return type, eg by turning f into a function template: C ++ 11中的一个简单解决方案是推迟返回类型的确定,例如将f转换为函数模板:

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

The definition of that function worries me a bit, though: 尽管如此,该功能的定义让我有点担心:

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

For the specializations of Wrapper where t.error() is not valid, the above f is a function template that cannot produce valid specializations. 对于Wrapper的特殊化,其中t.error()无效,上面的f是一个无法生成有效特化的函数模板。 This could fall under [temp.res]/8, which says that such templates are ill-formed, No Diagnostic Required: 这可能属于[temp.res] / 8,这表示此类模板格式错误,无需诊断:

If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required. 如果无法为模板生成有效的专门化,并且未实例化该模板,则模板格式错误,无需诊断。

However, I suspect that rule has been introduced to allow , but not require, implementations to check for errors in non-instantiated templates. 但是,我怀疑已引入规则允许但不要求实现检查非实例化模板中的错误。 In this case, there is no programming error in the source code ; 在这种情况下, 源代码中没有编程错误; the error would occur in instantiations of the class template described by the source code. 在源代码描述的类模板的实例化中会发生错误。 Therefore, I think it should be fine. 因此,我认为应该没问题。


An alternative solution is to use a fall-back return type to make the function declaration well-formed even if the definition is not (for all instantiations): 另一种解决方案是使用回退返回类型来使函数声明格式良好,即使定义不是(对于所有实例化):

#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();
    }
};

One approach is a tagged crtp. 一种方法是标记的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>{};

now f() just isn't there if T has no error() . 如果T没有error() ,那么f()就不存在了。

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

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