Por que o argumento do tipo de mapa C ++ requer um construtor vazio ao usar []?

98

Veja também lista padrão C ++ e tipos construtíveis padrão

Não é um problema importante, apenas irritante, pois não quero que minha classe seja instanciada sem os argumentos específicos.

#include <map>

struct MyClass
{
    MyClass(int t);
};

int main() {
    std::map<int, MyClass> myMap;
    myMap[14] = MyClass(42);
}

Isso me dá o seguinte erro g ++:

/usr/include/c++/4.3/bits/stl_map.h:419: erro: nenhuma função correspondente para chamada para 'MyClass ()'

Isso compila bem se eu adicionar um construtor padrão; Tenho certeza de que não é causado por sintaxe incorreta.

Nick Bolton
fonte
O código acima compila perfeitamente no MinGW (g ++ 3.4.5) e no MSVC ++ 2008, desde que um typedef para MyType seja fornecido e um ponto-e-vírgula anexado ao final da classe. Você deve estar fazendo outra coisa (por exemplo, ligar para a operadora [] conforme mencionado por bb) - poste o código completo .
j_random_hacker
Ah, sim, você está certo. Vai fazer.
Nick Bolton
Sim, sem o uso do myMap você não sabe o que precisa ser compilado para a classe do mapa. Qual provedor de biblioteca stl e versão também podem ajudar.
Greg Domjan

Respostas:

165

Este problema vem com o operador []. Citação da documentação SGI:

data_type& operator[](const key_type& k)- Retorna uma referência ao objeto que está associado a uma chave específica. Se o mapa ainda não contiver tal objeto, operator[] insere o objeto padrão data_type().

Se você não tem um construtor padrão, pode usar as funções inserir / localizar. O exemplo a seguir funciona bem:

myMap.insert( std::map< int, MyClass >::value_type ( 1, MyClass(1) ) );
myMap.find( 1 )->second;
Bayda
fonte
11
Excelente resposta - observe também emplaceem C ++ 11 como uma alternativa concisa para insert.
prideout de
3
Por que isso está std::<map>::value_typena insertchamada?
thomthom
1
Por que o construtor padrão precisa ser definido pelo usuário?
schuess 02 de
@schuess Não vejo razão para isso: = defaultdeve funcionar muito bem.
sublinhado_d
A condição 'Mapa ainda não contém tal objeto' seria avaliada em tempo de execução. Por que um erro de tempo de compilação?
Gaurav Singh
8

Sim. Os valores em contêineres STL precisam manter a semântica de cópia. IOW, eles precisam se comportar como tipos primitivos (por exemplo, int) o que significa, entre outras coisas, eles devem ser construtíveis por padrão.

Sem isso (e outros requisitos), seria desnecessariamente difícil implementar as várias operações internas de copiar / mover / trocar / comparar nas estruturas de dados com as quais os contêineres STL são implementados.

Após referência ao padrão C ++, vejo que minha resposta não foi precisa. A construção padrão não é, de fato, um requisito :

De 20.1.4.1:

O construtor padrão não é necessário. Certas assinaturas de função de membro de classe de contêiner especificam o construtor padrão como um argumento padrão. T () deve ser uma expressão bem definida ...

Então, estritamente falando, seu tipo de valor só precisa ser construtível padrão se você estiver usando uma função do contêiner que usa o construtor padrão em sua assinatura.

Os requisitos reais (23.1.3) de todos os valores armazenados em contêineres STL são CopyConstructiblee Assignable.

Existem também outros requisitos específicos para contêineres específicos, como ser Comparable(por exemplo, para chaves em um mapa).


A propósito, o seguinte é compilado sem erros no comeau :

#include <map>

class MyClass
{
public:
    MyClass(int t);
};

int main()
{
    std::map<int, MyClass> myMap;
}

Portanto, este pode ser um problema do g ++.

Assaf Lavie
fonte
2
Você acha que bb poderia estar descobrindo algo sobre a operadora []?
Nick Bolton
12
Esse código provavelmente compila porque você não está chamando myMap []
jfritz42
3

Verifique os requisitos do tipo armazenado do stl :: map. Muitas coleções de stl requerem que o tipo armazenado contenha algumas propriedades específicas (construtor padrão, construtor de cópia, etc.).

O construtor sem argumentos é necessário para o stl :: map, porque ele é usado quando o operador [] é chamado com a chave, que ainda não foi mantida pelo mapa. Neste caso, o operador [] insere a nova entrada que consiste na nova chave e valor construído usando o construtor sem parâmetros. E esse novo valor é então retornado.

oo_olo_oo
fonte
-2

Verifique se:

  • Você esqueceu o ';' após a declaração da classe.
  • MyType deveria ter sido declarado de acordo.
  • Nenhum construtor padrão lá ...

A declaração std :: map parece correta, eu acho.

Hernán
fonte
Compila bem se eu adicionar um construtor padrão.
Nick Bolton
-2

Provavelmente porque std :: pair requer isso. std :: pair contém dois valores usando a semântica de valor, então você precisa ser capaz de instanciá-los sem parâmetros. Portanto, o código usa std :: pair em vários lugares para retornar os valores do mapa ao chamador e isso é normalmente feito instanciando um par vazio e atribuindo os valores a ele antes de retornar o par local.

Você poderia contornar isso com ponteiros inteligentes usando um mapa <int, smartptr <MyClass>>, mas isso adiciona a sobrecarga de verificação de ponteiros nulos.

jmucchiello
fonte
2
+0. O par <T, U> pode ser usado perfeitamente com os tipos T e U sem construtores padrão - a única coisa que não pode ser usada neste caso é o próprio construtor padrão do par <T, U>. Nenhuma implementação de qualidade decente de map <K, V> usaria este construtor padrão porque ele limita o que K e V podem ser.
j_random_hacker