2015-12-19 11 views
5

Stavo cercando di pubblicare questo codice come risposta a this question, eseguendo questo puntatore wrapper (sostituendo il puntatore raw). L'idea è di delegare const al suo punto morto, in modo che la funzione filter non possa modificare i valori.g ++ e clang ++ - elimina il puntatore acquisito dall'ambiguità dell'operatore di conversione sovraccarico

#include <iostream> 
#include <vector> 

template <typename T> 
class my_pointer 
{ 
    T *ptr_; 

public: 
    my_pointer(T *ptr = nullptr) : ptr_(ptr) {} 

    operator T* &()    { return ptr_; } 
    operator T const*() const { return ptr_; } 
}; 

std::vector<my_pointer<int>> filter(std::vector<my_pointer<int>> const& vec) 
{ 
    //*vec.front() = 5; // this is supposed to be an error by requirement 
    return {}; 
} 

int main() 
{ 
    std::vector<my_pointer<int>> vec = {new int(0)}; 
    filter(vec); 
    delete vec.front(); // ambiguity with g++ and clang++ 
} 

Visual C++ 12 e 14 della compilazione questo senza un errore, ma GCC e Clang on Coliru rivendicazione che c'è un'ambiguità. Mi aspettavo che loro scegliessero il sovraccarico non-const std::vector::front e quindi my_pointer::operator T* &, ma no. Perché?

+0

provare a rendere il sovraccarico const anche un riferimento – bolov

+2

Fondamentalmente, lo standard dice che il compilatore deve prima decidere su cosa convertire e quindi eseguire la risoluzione di sovraccarico. Ma qui fallisce al primo passaggio perché 'int *' e 'const int *' sono entrambi consentiti dal contesto. –

risposta

8

[expr.delete]/1:

L'operando deve essere del puntatore di opporsi tipo o di tipo di classe. Se del tipo di classe , l'operando viene convertito contestualmente in modo implicito (clausola [conv]) in un puntatore al tipo di oggetto.

[conv]/5, sottolineatura mia:

Alcuni costrutti richiedono conversione ad un valore avente una di un insieme di tipi appropriati al costrutto specificato. Un espressione e di tipo classe E appare in questo contesto si dice essere contestualmente implicitamente convertito a un tipo specificato T e è ben formata se e solo se e può essere convertito in modo implicito un tipo T che è determinato come segue: E viene cercato per le funzioni di conversione non esplicite il cui tipo di reso è cv T o il riferimento a cv T tale che T è consentito dal contesto. Ci deve essere esattamente lo uno di questi T.

Nel codice, ci sono due T s (int * e const int *). È quindi mal formata, prima ancora di arrivare alla risoluzione di sovraccarico.


Si noti che c'è un cambiamento in quest'area tra C++ 11 e C++ 14. C++ 11 [expr.delete]/1-2 dice

L'operando deve avere un puntatore al tipo di oggetto, o un tipo di classe avente una sola funzione di conversione non esplicita (12.3.2) per un puntatore al tipo di oggetto. [...]

Se l'operando ha un tipo di classe, l'operando viene convertito in un tipo di puntatore chiamando la funzione di conversione di cui sopra, [...]

Il che, se letto letteralmente, autorizza il tuo codice e chiama sempre operator const int*() const, perché int* & è un tipo di riferimento, non un puntatore al tipo di oggetto. In pratica, le implementazioni considerano le funzioni di conversione "riferimento al puntatore all'oggetto" come operator int*&() e quindi rifiutano il codice perché ha più di una funzione di conversione non esplicita qualificata.

+0

Ho appena provato ad aggiungere la funzione 'get' e [ha funzionato] (http://coliru.stacked-crooked.com/a/67adffa698e65623), quindi questo accade solo con gli operatori di conversione impliciti? – LogicStuff

+1

@LogicStuff corretto. Chiamare una funzione membro fa solo una normale risoluzione di sovraccarico, e se restituisce un puntatore, 'delete' è felice. –

1

L'espressione delete accetta un'espressione cast come argomento, che può essere const o non.

vec.front() non è const, ma deve prima essere convertito in un puntatore per delete. Quindi entrambi i candidati const int* e int* sono possibili candidati; il compilatore non può scegliere quale vuoi.

Il più facile da fare è utilizzare un cast per risolvere la scelta. Per esempio:

delete (int*)vec.front(); 

Nota: funziona quando si utilizza una funzione get() invece di una conversione, perché le regole sono diverse. La scelta della funzione sovraccaricata si basa sul tipo dei parametri e dell'oggetto e non sul tipo di ritorno. Qui il non const è la funzione best viable come vec.front() non è const.

+1

Non penso che questa sia una risposta completa perché il compilatore sceglie sempre le versioni non-const delle funzioni per gli oggetti non-const per le normali chiamate di funzione. Non c'è ambiguità in quel caso, quindi perché dovrebbe esserci in questo caso? – Kevin

Problemi correlati