你好,對不起我的英文不好。g ++和clang ++與運算符<()重載的不同行爲
爲了使用C++ 11進行練習,我試圖編寫std :: experimental :: any(http://en.cppreference.com/w/cpp/experimental/any)類的一個版本,添加一些額外的東西。我在g ++(4.9.2)和clang ++(3.5.0)之間得到了不同的行爲。
以下是該類(以及使用的類)的縮寫版本,涵蓋了必需的最小值以及觸發問題的非常小的main()。
對不起,我沒有讓這個例子縮短。
#include <memory>
#include <iostream>
#include <type_traits>
#include <unordered_set>
namespace yans // yet another name space
{
class anyB // base for any
{
public:
virtual std::type_info const & typeT() const = 0;
virtual bool isLess (anyB const *) const = 0;
};
template <typename T>
class anyD : public anyB // derived for any
{
private:
T val;
static std::type_info const & typeInfo()
{ static auto const & ret = typeid(T); return ret; }
template <typename U> // preferred version
static auto lessF (U const & u1, U const & u2, int)
-> decltype( std::declval<U const &>()
< std::declval<U const &>())
{ return (u1 < u2); }
template <typename U> // emergency version
static auto lessF (U const &, U const &, ...) -> bool
{ throw std::runtime_error("no operator < for type "); }
public:
anyD (T const & v0)
: val(v0)
{ }
std::type_info const & typeT() const override final
{ return typeInfo(); }
bool isLess (anyB const * pB0) const override final
{
auto pD0 = dynamic_cast<anyD<T> const *>(pB0);
if (nullptr == pD0)
throw std::bad_cast();
return lessF(val, pD0->val, 0);
}
};
class any
{
private:
template <class T>
using sT = typename std::decay<T>::type;
template <class T>
using noAny
= typename std::enable_if
<false == std::is_same<any, sT<T>>::value, bool>::type;
template <class T>
using isCpCtr
= typename std::enable_if
<true == std::is_copy_constructible<sT<T>>::value,bool>::type;
std::unique_ptr<anyB> ptr;
static std::type_info const & voidInfo()
{ static auto const & ret = typeid(void); return ret; }
bool opLess (any const & a0) const
{
return
type().before(a0.type())
|| ( (type() == a0.type())
&& (false == empty())
&& ptr.get()->isLess(a0.ptr.get()));
}
public:
template <typename T, typename = noAny<T>, typename = isCpCtr<T>>
any (T && v0)
: ptr(new anyD<sT<T>>(std::forward<T>(v0)))
{ }
bool empty() const noexcept
{ return ! bool(ptr); }
std::type_info const & type() const
{ return (ptr ? ptr->typeT() : voidInfo()); }
friend bool operator< (any const &, any const &);
};
bool operator< (any const & a0, any const & a1)
{ return a0.opLess(a1); }
}
int main()
{
try
{
yans::any ai { 12 };
yans::any as { std::string("t1") };
yans::any au { std::unordered_set<int> { 1, 5, 3 } };
std::cout << "ai < 13 ? " << (ai < 13) << '\n';
std::cout << "as < std::string {\"t0\"} ? "
<< (as < std::string {"t0"}) << '\n';
std::cout << "au < std::unordered_set<int> { 2, 3, 4 } ? "
<< (au < std::unordered_set<int> { 2, 3, 4 }) << '\n';
}
catch (std::exception const & e)
{
std::cerr << "\nmain(): standard exception of type \""
<< typeid(e).name() <<"\"\n"
<< " ---> " << e.what() << " <---\n\n";
}
return EXIT_SUCCESS;
}
的想法,後面操作者<(),是返回「true」表示左操作數的類型是小於右側的類型(根據typeid的(T)。之前())和,如果類型匹配,則返回通過比較包含值返回的值。我知道這是一個值得懷疑的解決方案,但我正在學習。
麻煩的是,在類的任何實例中都可以包含沒有運算符<()的類型的值。在這個例子中,類std :: unordered_set < int>的一個實例。然後我試圖開發一些重載(SFINAE)方法lessF();當運算符<()可用於包含的類型T時,首選返回比較值;操作員<()不可用時使用的緊急版本會引發異常。
隨着鏗鏘聲++,我得到了我所希望的:類anyD < std :: unordered_set < int >>沒有實現lessF的首選版本,並且比較從緊急版本中產生了一個異常。
與G ++,與此相反,類anyD <的std :: unordered_set < INT >>生成優惠版本調用上的任何兩個實例操作<(),建立在所述兩個標準:: unordered_set < INT > lessF()的參數(any的模板化構造函數不是「顯式」定義的),然後遞歸調用自身,進入循環並生成一個錯誤(「Errore di segmentazione」,即「段錯誤」) 。
我想了解的是:
根據ISO C++ 11,這是正確的鏗鏘++的行爲或g的行爲++?我可以在本地阻止(僅在lessF())中,並且沒有聲明任何()的模板構造函數的「顯式」,兩個std :: unordered_set int>之間的比較將會在兩個嗎?換句話說:我怎樣才能防止在任何D < std :: unordered_set < int >>中開發lessF()的優先版本?
以下是兩個程序的輸出。
---- clang++ program output ----
ai < 13 ? 1
as < std::string {"t0"} ? 0
au < std::unordered_set<int> { 2, 3, 4 } ?
main(): standard exception of type "St13runtime_error"
---> no operator < for type <---
---- end output ----
---- g++ program output ----
ai < 13 ? 1
as < std::string {"t0"} ? 0
Errore di segmentazione
---- end output ----
@巴里,我認爲你是對的:這是一個可見性問題。 我把下列行虛擬類定義之前(anyB) '類中的任何;' '布爾操作符<(任何常量&A0,任何常量& a1);' ,現在還通過鐺產生++程序進入在一個循環,併產生分段錯誤 並祝賀你的例子:極其簡單 – max66
@ Barry,但關於你的解決方案......呃...在你的方式我失去了能力比較任何類的實例與通用類型T;如示例程序 我認爲將顯式模板構造函數(並開發模板特化對於任何對象遇到通用T對象時的操作符)。但是,在std :: experiment :: any的規範中,模板構造函數不是顯式的。 – max66
@ max66如果你按照我的建議去做,你仍然可以比較一個'any'和一個非'any'。 ADL會找到正確的'operator <'並執行轉換。 – Barry