You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用expression SFINAE时实现plain auto返回类型的行为?

问题与解决方案

背景代码与差异说明

先看这段代码:

struct Widget {
    int& get();
};

template<typename X>
auto func_1(X& x) {
  return x.get();
}

template<typename X>
auto func_2(X& x) -> decltype(x.get()) {
  return x.get();
}

当传入Widget类型的左值时:

  • func_1的返回类型是int——因为auto作为返回类型时,会对return表达式的结果做值退化,自动移除引用和顶层const限定
  • func_2的返回类型是int&——decltype(x.get())会直接保留表达式的原始类型(包括引用)

另外两者的关键差异是SFINAE支持:func_2具备表达式SFINAE特性,如果类型X没有.get()成员函数,func_2会直接被排除在重载解析之外;而func_1遇到这种情况会直接触发编译错误。

需求

需要实现一个函数,同时满足:

  1. 保留func_2的表达式SFINAE特性
  2. 拥有func_1的返回类型行为(即返回值退化后的类型)

目前自己实现的func_3看似可行,但想知道有没有更简洁的写法,以及它是否在所有场景下都和func_1的返回类型完全一致:

template<typename X>
auto func_3(X& x) -> std::remove_cvref_t<std::decay_t<decltype(x.get())>> {
  return x.get();
}

简洁可行的方案

方案一:用std::decay_t简化尾置返回类型

std::decay_t<T>的行为完全对应auto作为返回类型时的类型推导规则:它会移除引用、顶层const/volatile限定,同时将数组转为指针、函数转为函数指针。所以直接用它来包裹decltype(x.get())即可,不需要嵌套多层类型转换:

#include <type_traits>

template<typename X>
auto func_4(X& x) -> std::decay_t<decltype(x.get())> {
  return x.get();
}

这个写法的返回类型和func_1完全一致,同时保留了func_2的SFINAE特性——只要x.get()表达式不合法,函数就会被排除在重载解析之外。

方案二:C++14+的模板参数SFINAE写法

如果使用C++14及以上标准,还可以通过模板参数的默认值来实现SFINAE,同时用auto直接推导返回类型,写法更简洁:

#include <type_traits>

template<typename X, typename = decltype(std::declval<X&>().get())>
auto func_5(X& x) {
  return x.get();
}

这里通过std::declval<X&>().get()来检查X是否具备.get()成员,不满足的话模板会被SFINAE掉;返回类型用auto推导,和func_1完全一致。

关于func_3的冗余性

func_3里的std::remove_cvref_t<std::decay_t<...>>是冗余的,因为std::decay_t<T>本身已经包含了std::remove_cv_t<std::remove_reference_t<T>>的逻辑(针对非数组/函数类型),外层再套std::remove_cvref_t不会改变最终的类型结果。

内容的提问来源于stack exchange,提问作者levzettelin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:18:31