从函数内部推断函数返回类型
Infere the function return type from inside the function
我有一个class
,非常遗憾,它依赖于两步初始化。这意味着在构建之后,对象仍然没有准备好使用,除非调用了初始化方法:
class A
{
public:
A();
bool init();
private:
bool m_is_initialized;
};
类的每个其他方法都应遵循此策略:如果在类尚未初始化时调用该方法,则该方法应停止执行,并在类特定的通道上记录错误。
问题是有些方法有返回类型。这种情况下的策略是返回返回类型的默认构造值。
这个想法是有一个简单的宏,可以在每个方法实现的开始调用,比如:
#define CHECK_INITIALIZED
if ( !m_is_initialized )
{
LOG_E( m_channel, "%s object not initialized.", __PRETTY_FUNCTION__ );
assert( false );
return;
}
顺便说一句,return
语句仅对void
函数有效,并不适用于所有情况。有没有一种方法可以推断出宏在其中展开的函数的返回类型T
,这样我就可以返回T()
,并使宏在任何地方都可用?
编辑:请注意,由于项目限制,遗憾的是不能使用异常。
为什么要使用宏?模板可以很好地处理这个问题。
struct has_init_check
{
protected:
template<class F>
auto if_ready(F&& f)
{
if (m_is_initialized)
{
f();
}
else
{
// log the error here
}
}
void notify_initialized()
{
m_is_initialized = true;
}
private:
bool m_is_initialized = false;
};
class A
: has_init_check
{
public:
A();
bool init() { notify_initialized(); }
int foo();
void bar();
};
int A::foo()
{
int result = 0; // default value
if_ready([&]
{
// logic here
result = 10;
});
return result;
}
void A::bar()
{
if_ready([]
{
// logic here
});
}
您可以使用return {};
来表示您想要一个默认的初始化返回类型。尽管在类型不是默认可构造的、您试图返回引用或返回类型为void
的情况下,这种方法还是失败了。
另一种选择是使用boost::optional
/std:optional
作为所有函数与return {};
组合的返回类型。这允许您在不执行任何操作时返回默认的可选项(因此可选项为空)。
另一种选择是将返回值传递给宏,并将其用于之类的返回
#define CHECK_INITIALIZED(default_return_value)
if ( !m_is_initialized )
{
LOG_E( m_channel, "%s object not initialized.", __PRETTY_FUNCTION__ );
return default_return_value;
}
另一个答案,另一种方法。
不允许出现异常,但我们仍然可以通过使用变量(错误、对象)在构建时发现初始化失败。
我们知道,我们对象的所有成员都是notrow_constructable(这是一个约束)。因此,它们也必须是不可移动的。
因此,我们可以使用variant和optional的组合来管理对象构造,并在失败时记录/停止。
现在,在实现方法时没有运行时开销。
#include <variant>
#include <optional>
#include <string>
#include <cstdlib>
#include <iostream>
#include <type_traits>
struct construction_error
{
construction_error(std::string s)
: message_(s)
{}
const std::string message() const {
return message_;
}
std::string message_;
};
template<class T> using construction_result = std::variant<construction_error, T>;
template<class T> struct tag {};
template<class T, class...Args>
auto construct(tag<T>, Args&&...args) -> construction_result<T>
{
auto x = T(std::forward<Args>(args)...);
if (auto result = x.init())
{
return std::move(result).value();
}
else
{
return std::move(x);
}
}
class A
{
public:
A() noexcept { std::cout << "speculative construction" << std::endl; }
std::optional<construction_error> init() noexcept {
if (rand() < RAND_MAX / 2)
{
return construction_error("failed to construct an A");
}
else
{
// complete the construction
return {};
}
}
int foo();
void bar();
};
int A::foo()
{
std::cout << __func__ << std::endl;
// logic here
return 10;
}
void A::bar()
{
std::cout << __func__ << std::endl;
// logic here
}
void do_thing(A a, A b, A c)
{
a.foo();
b.foo();
c.foo();
a.bar();
b.bar();
c.bar();
}
template<class T>
void maybe_report_failure(const T&)
{
}
void maybe_report_failure(construction_error const& cf)
{
std::cout << "construction failure: " << cf.message() << std::endl;
}
int main()
{
for (int i = 0 ; i < 100 ; ++i)
{
auto maybe_a_1 = construct(tag<A>());
auto maybe_a_2 = construct(tag<A>());
auto maybe_a_3 = construct(tag<A>());
auto action = [](auto&&...as)
{
constexpr bool good = (std::is_same_v<std::decay_t<decltype(as)>, A> && ...);
if constexpr (good)
{
do_thing(std::move(as)...);
}
else
{
(maybe_report_failure(as), ...);
}
};
std::visit(action,
std::move(maybe_a_1),
std::move(maybe_a_2),
std::move(maybe_a_3));
}
}
http://coliru.stacked-crooked.com/a/397427a89afa728a
这应该适用于void、默认可构造+可移动和引用类型(未测试:):
#define CHECK_INITIALIZED_WITH_RETURN(R)
if ( !m_is_initialized )
{
LOG_E( m_channel, "%s object not initialized.", __PRETTY_FUNCTION__ );
assert( false );
static std::conditional_t<std::is_same_v<R,void>,int,std::decay_t<R>> some_default{};
return R(some_default);
}
其中R可以是空的,T,T&,。。。(假设默认构建的静态没有令人讨厌的副作用,并且以"理智"的方式使用…)
- 检查函数返回类型是否与STL容器类型值相同
- 警告:在函数返回类型 [-Wignore 限定符] 时忽略类型限定符
- 在 c++ 中将函数返回类型指定为模板参数
- 通过引用传递参数;函数返回类型是否必须为 VOID?
- 如何避免模板函数返回类型重复?
- 从类型bankAccount的返回值到函数返回类型int没有可行的转换
- 为什么在某些情况下从函数返回类型中删除 cv 限定符?
- 模板类内模板类的函数返回类型
- 为什么函数返回类型中不允许参数推导?
- 函数返回类型之前的"define"
- C++推断要隐式调用的模板函数返回类型
- 具有不同模板参数的函数返回类型
- 当函数返回类型为父类时,如何返回子类的对象?
- C++:在原型中声明"auto"函数返回类型仍然会导致在扣除错误之前使用"auto&quo
- C++14 'auto'能够获取函数返回类型,我们还需要 std::result_of<> 吗?
- 函数返回类型中的模板类型推断
- 嵌套模板类返回类型在 C++ 中的头文件中函数返回类型的语法
- C++重写 void 函数返回类型会导致生成失败
- 从函数内部推断函数返回类型
- C++嵌套类函数返回类型和命名空间