簡體   English   中英

如何確定一個類是否實現!=運算符重載?

[英]How to determine if a class implements != operator overloading?

鑒於以下功能:

template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
  if (a.size() != b.size())
    return false;

  for (size_t i = 0; i < a.size(); ++i)
    if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
      return false;

  return true;
}

如何確定給定的T是否覆蓋!=運算符? 如果有人使用沒有重載的類型,它可能最終會使用簡單的二進制比較,這可能會導致錯誤的結果。 所以我想確保只能在這里使用具有自己的!=運算符重載的類。

[更新1 - 提升'半'解決方案]

我意識到,如果你的類有轉換操作符(對於允許!= comparision的類型),我的第一個答案中的示例不起作用,要修復它你可以使用boost has_not_equal_to type trait。 仍然不完美,因為在某些情況下它會產生編譯錯誤而不是給出值。 這些案例列在提供的鏈接中。

[更新2 - 概念解決方案]

使用概念的示例:

#include <iostream>
#include <vector>

template<typename T>
concept bool OperatorNotEqual_comparable()
{
    return requires (T a, T b) {
        { a.operator!=(b) } -> bool;
    };   
}

template <OperatorNotEqual_comparable T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
  if (a.size() != b.size())
    return false;

  for (size_t i = 0; i < a.size(); ++i)
    if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
      return false;

  return true;
}

struct sample1{
    bool operator!=(const sample1&) const { return true; }
};

struct sample2{
};

struct sample3{
    operator void*() { return 0; }
};

int main() {
    // Compiles ok!
    std::vector<sample1> vec1;
    equals(vec1, vec1);

    // Fails, which is OK!
    //std::vector<sample2> vec2;
    //equals(vec2, vec2);    

    // Fails, which is OK!
    //std::vector<sample2*> vec2;
    //equals(vec2, vec2);

    // Fails, which is OK!
    //std::vector<int> vec4;
    //equals(vec4, vec4);        

    // Fails, which is OK!
    //std::vector<sample3> vec5;
    //equals(vec5, vec5);            
}

http://melpon.org/wandbox/permlink/txliKPeMcStc6FhK

[舊答案 - SFINAE解決方案,不檢查轉換運算符]

您可以使用SFINAE,並在不久的將來概念(它們在gcc 6.0中),

#include<iostream>
#include<string>
#include<type_traits>

template<typename T>
class has_not_equal{
    template<typename U>
    struct null_value{
        static U& value;
    };

    template<typename U>
    static std::true_type test(U*,decltype(null_value<U>::value!=null_value<U>::value)* = 0);
    static std::false_type test(void*);

public:
    typedef decltype(test(static_cast<T*>(0))) type;
    static const bool value = type::value;
};

struct sample1{
    bool operator!=(const sample1&) { return true; }
};

struct sample2{
};

int main(){
    std::cout<<std::boolalpha;
    std::cout<<has_not_equal<int>::value<<std::endl;
    std::cout<<has_not_equal<std::string>::value<<std::endl;

    std::cout<<has_not_equal<sample1>::value<<std::endl;
    std::cout<<has_not_equal<sample2>::value<<std::endl;
}

輸出:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
true
true
true
false

生活

上面的代碼是來自這個站點的修改版本,它是為operator== ,我把它改為operator!=

如果T沒有operator != ,則模板不會為該類型實例化,但是您將從編譯器中獲得(可能非常長且不可讀)錯誤消息。 在另一方面,它T有一個operator != ,那么它應該是蠻好用它。 除非T s operator !=無論如何被打破,否則不會出現無聲的錯誤結果。

唯一的另一個(除了轉換)情況(我能想到)可以發生二進制比較(有operator!=定義)並導致無效的錯誤結果是當T實際上是一個指針而你期望進行“深度比較”時。

可以為包含指針的向量添加重載但不包括指向數組存儲的指針。

template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) 
{
  return std::equal(a.begin(), a.end(), b.begin());
}

template <typename T>
static bool equals(const std::vector<T *> &a, const std::vector<T *> &b) 
{
  return std::equal(a.begin(), a.end(), b.begin() 
    [](T* ap, T* bp) -> bool { return *ap == *bp; });
}

如果你有興趣檢查是否有一個操作符!=在一個類中定義(並且它已經准確地給出了簽名),你可能需要這種方法(測試來自marcinj的代碼):

#include <iostream>
#include <vector>
#include <type_traits>

template <class T, class = void>
struct has_not_equal: std::false_type { };

template <class T>
struct has_not_equal<T, typename std::enable_if<std::is_same<decltype(static_cast<bool (T::*)(const T&)const>(&T::operator!=)), bool (T::*)(const T&)const>::value>::type >: std::true_type { };

struct sample1{
    bool operator!=(const sample1&) const { return true; }
};
struct sample2{
};
struct sample3:sample2 {
  bool operator!=(const sample2& b) const { return true; }
};

struct sample4:sample2 {
  bool operator!=(const sample2& b) const { return true; }
  bool operator!=(const sample4& b) const { return true; }
};

int main(){

    std::cout<<std::boolalpha;
    std::cout<<has_not_equal<int>::value<<std::endl;
    std::cout<<has_not_equal<std::string>::value<<std::endl;

    std::cout<<has_not_equal<sample1>::value<<std::endl;
    std::cout<<has_not_equal<sample2>::value<<std::endl;
    std::cout<<has_not_equal<sample3>::value<<std::endl;
    std::cout<<has_not_equal<sample4>::value<<std::endl;
}

計划的輸出:

false
false
true
false
false
true

您可以通過添加has_not_equal結構的特化來輕松添加允許的operator!=重載...

即使我的問題的標題在這里說我正在尋找一種方法來確定是否定義了!=運算符,真正的問題(正如你可以從描述和我的評論中讀到的)是如何確保我不對於沒有!=運算符定義的類型T,默默地得到錯誤的結果。 現有的答案帶來了好處,但真正的答案是:

1) if (a[i] != b[i])使用缺少!=運算符覆蓋的類型(例如std::vector<sample2>使用值類型數組實例化模板,則會在if (a[i] != b[i])行上出現編譯器錯誤來自marcinj的答案的std::vector<sample2> )如果你使用std::equal你會很難理解編譯器錯誤。 在這種情況下,顯式比較在尋找解決方案時更有幫助。

2)如果你在向量中有一個引用類型進行比較,你將首先得到沒有問題(因為有為參考定義的比較運算符,即使它們只通過地址值進行平面比較)。 如果您希望確保比較的工作方式就像您使用了值類型(包括深度比較)那么為Pixelchemist指出的向量添加一個帶指針類型的特化。 但是,我現在無法編譯std :: equals變種。

最后,適合我的解決方案是這樣的:

template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
  if (a.size() != b.size())
    return false;

  for (size_t i = 0; i < a.size(); ++i)
    if (a[i] != b[i])
      return false;

  return true;
}

template <typename T>
static bool equals(const std::vector<T*> &a, const std::vector<T*> &b) {
  if (a.size() != b.size())
    return false;

  for (size_t i = 0; i < a.size(); ++i)
    if (*a[i] != *b[i])
      return false;

  return true;
}

使用marcinj的答案中的sample1 / 2/3/4結構進行測試。 重要的是要注意,運算符重載必須使用值類型( operator == (const sample1&) )而不是引用類型。

我仍然贊成所有給出有用信息的答案,以獲得最終答案。

如果有人使用沒有重載的類型,它可能最終會使用簡單的二進制比較,這可能會導致錯誤的結果。

在C ++中沒有“二進制比較”這樣的東西。 您的類/結構有operator!=或模板不會被實例化(如果沒有其他候選項,那么它將是一個錯誤)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM