能否让C++ Lambda自动推导std::variant类型的返回值?
std::variant of All Returned Types? 你猜的没错——当前C标准(包括C23)并没有原生支持这种auto_variant式的自动推导,但我们可以先聊聊为什么默认不行,再看看有没有接近需求的实用技巧。
Why Default Deduction Fails
Lambda的自动返回类型推导规则很严格:它要求所有返回路径的类型必须能隐式转换为同一个单一类型。在你的例子里,A和B是完全无关的结构体,没有共同的可转换类型,所以直接写[]() { return A{}; else return B{}; }会直接编译报错——编译器根本无法确定要推导成什么类型。
Workarounds to Get Close to What You Want
虽然不能完全自动推导,但我们可以用一些技巧简化代码,避免重复写出std::variant<A, B>:
1. Use a Helper Macro (Simplest Approach)
如果只是想减少重复书写,可以定义一个宏来自动生成variant返回类型:
#define AUTO_VARIANT(...) []() -> std::variant<__VA_ARGS__> // 使用方式 auto var = AUTO_VARIANT(A, B) { if (rand()%2 ){ return A{}; } else { return B{}; } }();
这和你原来的写法本质一样,但把重复的类型声明简化成了宏调用。
2. Wrap the Lambda with a Template
如果更倾向于模板而非宏,可以写一个辅助模板来包装你的Lambda,把variant类型参数提到前面:
#include <iostream> #include <variant> #include <utility> struct A{ int val = 47; }; struct B { float val = 4.7; }; template<typename... ReturnTypes> auto make_variant_lambda(auto&& func) { return [func = std::forward<decltype(func)>(func)]() -> std::variant<ReturnTypes...> { return func(); }; } int main() { for (int i=0;i<8;++i){ auto generator = make_variant_lambda<A, B>([]() { if (rand()%2 ){ return A{}; } else { return B{}; } }); auto var = generator(); std::visit ([](const auto& val) {std::cout<< val.val << std::endl;}, var); } }
这依然需要你显式列出返回类型,但把类型声明和Lambda逻辑分离开了。
3. Restructure to Emplace into a Predefined Variant
如果可以调整代码结构,也可以预先定义variant,然后在循环里直接emplace不同类型:
int main() { std::variant<A, B> var; for (int i=0;i<8;++i){ if (rand()%2 ){ var.emplace<A>(); } else { var.emplace<B>(); } std::visit ([](const auto& val) {std::cout<< val.val << std::endl;}, var); } }
这种写法完全避免了在Lambda里处理variant类型,适合简单的场景。
Why std::any Isn't a Fit
你提到std::any不符合需求是完全正确的:std::any是运行期类型擦除,类型信息在编译期是不确定的,必须用any_cast在运行期验证类型——这和你想要编译期确定类型集合的需求完全相悖。
Final Verdict
目前没有任何原生C++特性能让编译器自动从Lambda的多返回路径推导返回类型为对应的std::variant。所有可行的方案都需要你显式列出所有可能的返回类型,只是写法上可以更简洁一些。
内容的提问来源于stack exchange,提问作者NoSenseEtAl

