"Ambigous" 等效转换 - 我可以让编译器只选择任何一个吗?
"Ambigous" equivalent conversions - can I make the compiler just choose any?
我有一个模拟指针行为的 class 模板,return在转换为任何指针类型时将指针指向给定的存储内存位置。
它具有转换为多种类型的运算符(intptr_t,它正在模拟的本机指针等),对于指针来说是正常的,并且它 return 的值是一个真正的指针 return 那样的话。
MockPointer<int> TestIntPointer;
MockPointer<int> TestIntPointer2;
int* a = nullptr;
ASSERT_TRUE(TestIntPointer == a);
ASSERT_TRUE(TestIntPointer == TestIntPointer2);
没有 bool operator==(MockPointer<T> rhs)
我在第二个断言时遇到错误:
Multiple markers at this line
- operator==(int*, int*) <built-in>
- candidates are:
- in expansion of macro 'ASSERT_TRUE'
- operator==(intptr_t {aka long int}, intptr_t {aka long int}) <built-in>
- operator==(intptr_t {aka long int}, int) <built-in>
- operator==(int, intptr_t {aka long int}) <built-in>
- operator==(int, int) <built-in>
ambiguous overload for 'operator==' (operand types are '{anonymous}::MockPointer<int> and '{anonymous}::MockPointer<int>)
对于运算符,第一个断言失败:
- in expansion of macro 'ASSERT_TRUE'
- ambiguous overload for 'operator==' (operand types are '{anonymous}::MockPointer<int>' and 'int*')
- candidates are:
- operator==(int*, int*) <built-in>
我知道有很多方法可以转换变量并进行比较!为了比较,它们都工作相同!我能否强制编译器以某种方式选择第一个可用的转换并继续使用它,而不是抱怨它无法决定它是否应该在茶中加入糖和牛奶,或者更确切地说是牛奶和糖?
编辑:完整代码(Operator== 行接近文件的 2/3,一些被注释掉):
#pragma once
#include <stdint.h> //zapewnia stdint_t, specjalny int do przechowywania wskaźników.
#include "api/utils/tinyutils.h"
#include "api/standard_exceptions.h"
namespace Api {
/**
* ShmPointer
* Klasa zachowująca się jak zwykły wskaźnik ale działająca dla w pamięci współdzielonej.
*
* Sposób użycia:
* Każda instancja (wariant) pamięci współdzielonej tworzona jest bądź pobierana przez ShmProvider
* W szczególności określona instancja to
*
* ShmProvider<typ_shm, klucz_shm> MyShmProvider;
*
* Dla niej generujemy template pointerów do wszystkiego, co można w tejże pamięci znaleźć:
*
* template <class T>using MyShmPointer = ShmPointer<T,MyShmProvider>;
*
* Potem już tylko:
* MyShmPointer<Grupa> gr = &(MyShmProvider::Base->Grupy[5]); //cast zwykłego wskaźnika na ShmPointer
*
* MyShmProvider::Base->AktywnaGrupa = gr; //przypisanie jednego ShmPointer do drugiego, leżącego w SHM
*
* // w zupełnie innym programie
* display(MyShmProvider::Base->AktywnaGrupa->kolor); //odczytanie wartości po wskaźniku
*/
template<class T, class ShmProvider>
class ShmPointer
{
private:
int offset = -1;
bool IsEmpty() const {
return offset == -1;
}
T* MyAddress() {
if(unlikely(IsEmpty()))
return nullptr;
return (T*)((char*)ShmProvider::Base + offset);
}
T* Add(int count) {
return reinterpret_cast<T*>((char*)MyAddress() + count*sizeof(T));
}
public:
ShmPointer() = default;
//Konstruktor na podstawie wskaźnika.
ShmPointer(T* shmaddr) : offset((char*)shmaddr - (char*)ShmProvider::Base) {
}
// Operatory rzutowania
//Cast na typ "wskaźnik do T"
operator T*() {
return MyAddress();
}
//Cast na typ boolean
operator bool() {
return !IsEmpty();
}
//rzutowanie na int
operator intptr_t() {
return (intptr_t) MyAddress();
}
// Standardowe operatory (znaczki)
//Dereferencja na pole
T* operator->()
{
if(IsEmpty()) throw( NullPointerDereferenceException(LOCATION) );
return MyAddress();
}
//Dereferncja wprost
T& operator*()
{
if(IsEmpty()) throw( NullPointerDereferenceException(LOCATION) );
return *(MyAddress());
}
//Przypisanie
T* operator=(T* shmaddr)
{
if(shmaddr==nullptr)
{
offset=-1;
return nullptr;
}
offset = (char*)shmaddr - (char*)ShmProvider::Base;
return MyAddress();
}
//Dereferencja nawiasami
T& operator[](int ndx)
{
if(IsEmpty()) throw(NullPointerDereferenceException(LOCATION));
return (T&) *(Add(ndx));
}
T* operator+(int ndx)
{
if(IsEmpty())
return nullptr;
return Add(ndx);
}
T* operator-(int arg)
{
if(IsEmpty())
return nullptr;
return Add(-arg);
}
intptr_t operator-(T* arg)
{
if(MyAddress() == nullptr){ return 0; }
return (intptr_t)((char*)MyAddress() - (char*)arg);
}
bool operator!()
{
return offset == -1;
}
bool operator==(const Api::ShmPointer<T, ShmProvider>& rhs) const {
return offset == rhs.offset;
}
bool operator==(const std::nullptr_t rhs) const {
return IsEmpty();
}
/*
bool operator==(const T* rhs) const {
return (MyAddress() == rhs);
}
*/
/*
bool operator!=(const ShmPointer<T, ShmProvider>& rhs) const {
return !(*this == rhs);
}
*/
bool operator!=(const std::nullptr_t rhs) const {
return !(*this == rhs);
}
/*
bool operator!=(const T* rhs) const {
return !(MyAddress() == rhs);
}
*/
T* operator++()
{
if(IsEmpty())
return nullptr;
offset += sizeof(T);
return MyAddress();
}
T* operator++(int)
{
if(IsEmpty())
return nullptr;
T* dummy = MyAddress();
offset += sizeof(T);
return dummy;
}
T* operator--()
{
if(IsEmpty()) return nullptr;
offset -= sizeof(T);
return MyAddress();
}
T* operator--(int)
{
if(IsEmpty()) return nullptr;
T* dummy = MyAddress();
offset -= sizeof(T);
return dummy;
}
};
}
这是由于大量的转换运算符和通过构造函数进行的隐式转换。你应该能够通过
避免歧义
提供一个完美匹配的比较运算符
bool operator==(const MockPointer<T>& rhs);
使构造函数采用 T*
显式
explicit MockPointer(T*);
我在 http://coliru.stacked-crooked.com/a/73300327b4fcfac8 做了一个实例,你可以玩一下;尝试从构造函数中删除 explicit
关键字,这会重现您在问题和评论中描述的问题。允许编译器对隐式转换有太多自由度通常会导致歧义,因为编译器只是有太多方法来进行函数调用。
您还可以从删除一个或多个隐式转换中获得一些结果。
我有一个模拟指针行为的 class 模板,return在转换为任何指针类型时将指针指向给定的存储内存位置。
它具有转换为多种类型的运算符(intptr_t,它正在模拟的本机指针等),对于指针来说是正常的,并且它 return 的值是一个真正的指针 return 那样的话。
MockPointer<int> TestIntPointer;
MockPointer<int> TestIntPointer2;
int* a = nullptr;
ASSERT_TRUE(TestIntPointer == a);
ASSERT_TRUE(TestIntPointer == TestIntPointer2);
没有 bool operator==(MockPointer<T> rhs)
我在第二个断言时遇到错误:
Multiple markers at this line
- operator==(int*, int*) <built-in>
- candidates are:
- in expansion of macro 'ASSERT_TRUE'
- operator==(intptr_t {aka long int}, intptr_t {aka long int}) <built-in>
- operator==(intptr_t {aka long int}, int) <built-in>
- operator==(int, intptr_t {aka long int}) <built-in>
- operator==(int, int) <built-in>
ambiguous overload for 'operator==' (operand types are '{anonymous}::MockPointer<int> and '{anonymous}::MockPointer<int>)
对于运算符,第一个断言失败:
- in expansion of macro 'ASSERT_TRUE'
- ambiguous overload for 'operator==' (operand types are '{anonymous}::MockPointer<int>' and 'int*')
- candidates are:
- operator==(int*, int*) <built-in>
我知道有很多方法可以转换变量并进行比较!为了比较,它们都工作相同!我能否强制编译器以某种方式选择第一个可用的转换并继续使用它,而不是抱怨它无法决定它是否应该在茶中加入糖和牛奶,或者更确切地说是牛奶和糖?
编辑:完整代码(Operator== 行接近文件的 2/3,一些被注释掉):
#pragma once
#include <stdint.h> //zapewnia stdint_t, specjalny int do przechowywania wskaźników.
#include "api/utils/tinyutils.h"
#include "api/standard_exceptions.h"
namespace Api {
/**
* ShmPointer
* Klasa zachowująca się jak zwykły wskaźnik ale działająca dla w pamięci współdzielonej.
*
* Sposób użycia:
* Każda instancja (wariant) pamięci współdzielonej tworzona jest bądź pobierana przez ShmProvider
* W szczególności określona instancja to
*
* ShmProvider<typ_shm, klucz_shm> MyShmProvider;
*
* Dla niej generujemy template pointerów do wszystkiego, co można w tejże pamięci znaleźć:
*
* template <class T>using MyShmPointer = ShmPointer<T,MyShmProvider>;
*
* Potem już tylko:
* MyShmPointer<Grupa> gr = &(MyShmProvider::Base->Grupy[5]); //cast zwykłego wskaźnika na ShmPointer
*
* MyShmProvider::Base->AktywnaGrupa = gr; //przypisanie jednego ShmPointer do drugiego, leżącego w SHM
*
* // w zupełnie innym programie
* display(MyShmProvider::Base->AktywnaGrupa->kolor); //odczytanie wartości po wskaźniku
*/
template<class T, class ShmProvider>
class ShmPointer
{
private:
int offset = -1;
bool IsEmpty() const {
return offset == -1;
}
T* MyAddress() {
if(unlikely(IsEmpty()))
return nullptr;
return (T*)((char*)ShmProvider::Base + offset);
}
T* Add(int count) {
return reinterpret_cast<T*>((char*)MyAddress() + count*sizeof(T));
}
public:
ShmPointer() = default;
//Konstruktor na podstawie wskaźnika.
ShmPointer(T* shmaddr) : offset((char*)shmaddr - (char*)ShmProvider::Base) {
}
// Operatory rzutowania
//Cast na typ "wskaźnik do T"
operator T*() {
return MyAddress();
}
//Cast na typ boolean
operator bool() {
return !IsEmpty();
}
//rzutowanie na int
operator intptr_t() {
return (intptr_t) MyAddress();
}
// Standardowe operatory (znaczki)
//Dereferencja na pole
T* operator->()
{
if(IsEmpty()) throw( NullPointerDereferenceException(LOCATION) );
return MyAddress();
}
//Dereferncja wprost
T& operator*()
{
if(IsEmpty()) throw( NullPointerDereferenceException(LOCATION) );
return *(MyAddress());
}
//Przypisanie
T* operator=(T* shmaddr)
{
if(shmaddr==nullptr)
{
offset=-1;
return nullptr;
}
offset = (char*)shmaddr - (char*)ShmProvider::Base;
return MyAddress();
}
//Dereferencja nawiasami
T& operator[](int ndx)
{
if(IsEmpty()) throw(NullPointerDereferenceException(LOCATION));
return (T&) *(Add(ndx));
}
T* operator+(int ndx)
{
if(IsEmpty())
return nullptr;
return Add(ndx);
}
T* operator-(int arg)
{
if(IsEmpty())
return nullptr;
return Add(-arg);
}
intptr_t operator-(T* arg)
{
if(MyAddress() == nullptr){ return 0; }
return (intptr_t)((char*)MyAddress() - (char*)arg);
}
bool operator!()
{
return offset == -1;
}
bool operator==(const Api::ShmPointer<T, ShmProvider>& rhs) const {
return offset == rhs.offset;
}
bool operator==(const std::nullptr_t rhs) const {
return IsEmpty();
}
/*
bool operator==(const T* rhs) const {
return (MyAddress() == rhs);
}
*/
/*
bool operator!=(const ShmPointer<T, ShmProvider>& rhs) const {
return !(*this == rhs);
}
*/
bool operator!=(const std::nullptr_t rhs) const {
return !(*this == rhs);
}
/*
bool operator!=(const T* rhs) const {
return !(MyAddress() == rhs);
}
*/
T* operator++()
{
if(IsEmpty())
return nullptr;
offset += sizeof(T);
return MyAddress();
}
T* operator++(int)
{
if(IsEmpty())
return nullptr;
T* dummy = MyAddress();
offset += sizeof(T);
return dummy;
}
T* operator--()
{
if(IsEmpty()) return nullptr;
offset -= sizeof(T);
return MyAddress();
}
T* operator--(int)
{
if(IsEmpty()) return nullptr;
T* dummy = MyAddress();
offset -= sizeof(T);
return dummy;
}
};
}
这是由于大量的转换运算符和通过构造函数进行的隐式转换。你应该能够通过
避免歧义提供一个完美匹配的比较运算符
bool operator==(const MockPointer<T>& rhs);
使构造函数采用
T*
显式explicit MockPointer(T*);
我在 http://coliru.stacked-crooked.com/a/73300327b4fcfac8 做了一个实例,你可以玩一下;尝试从构造函数中删除 explicit
关键字,这会重现您在问题和评论中描述的问题。允许编译器对隐式转换有太多自由度通常会导致歧义,因为编译器只是有太多方法来进行函数调用。
您还可以从删除一个或多个隐式转换中获得一些结果。