2017-10-13 1218 views
0

我有一个class,非常不幸,它依赖于两步初始化。这意味着,正在兴建后,对象是还没有准备好被使用,除非初始化方法被调用:Infere函数返回类型

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(),并使宏在任何地方都可用?

编辑: 请注意,由于项目限制,不幸的是例外是不可用的。

+1

值初始化的返回值不会有效 一般来说。有些类型不是默认构造的,有些则是有效的返回值。例如,一个函数可以返回0或者“”或者返回成功的值。 – chris

+4

而不是'返回'为什么不'抛出'? – NathanOliver

+0

@chris我知道,但只要伐木发生,我们就很好。 – nyarlathotep108

回答

0

你可以使用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;                \ 
} 
+0

会返回{};编译如果函数“返回”无效? – roalz

+0

@roalz,不,但是你可以有一个'std :: optional '或'boost :: optional '来使它统一。 – chris

+0

@roalz不,我已经更新了包含该答案的答案。 – NathanOliver

3

都为什么要使用宏的回报?模板将很好地处理这个问题。

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 
    }); 
} 
+0

宏主要用于记录功能名称。 – nyarlathotep108

0

另一个答案,另一种方法。

异常是不允许的,但我们仍然可以通过使用变体(错误,对象)来捕获构建时的初始化失败。

我们知道我们对象的所有成员都是nothrow_constructible(这是一个约束)。因此它们也必须是不可移动的。

因此,我们可以使用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

0

这应该适用于两个空隙,缺省构造+可动的,和引用类型(未测试:)):

#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 (假设默认的静态构造没有令人讨厌的副作用,并且它以'理智'的方式使用......)