2017-10-13 1274 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 (假設默認的靜態構造沒有令人討厭的副作用,並且它以'理智'的方式使用......)