błąd: prośba o członka „..” w „..”, który jest typu nieklasowego

440

Mam klasę z dwoma konstruktorami, jednym, który nie przyjmuje żadnych argumentów, i drugim, który przyjmuje jeden argument.

Tworzenie obiektów za pomocą konstruktora, który przyjmuje jeden argument, działa zgodnie z oczekiwaniami. Jeśli jednak tworzę obiekty za pomocą konstruktora, który nie przyjmuje argumentów, pojawia się błąd.

Na przykład, jeśli skompiluję ten kod (używając g ++ 4.0.1) ...

class Foo
{
  public:
    Foo() {};
    Foo(int a) {};
    void bar() {};
};

int main()
{
  // this works...
  Foo foo1(1);
  foo1.bar();

  // this does not...
  Foo foo2();
  foo2.bar();

  return 0;
}

... pojawia się następujący błąd:

nonclass.cpp: In function int main(int, const char**)’:
nonclass.cpp:17: error: request for member bar in foo2’, which is of non-class type Foo ()()’

Dlaczego tak się dzieje i jak to działa?

Sarnesjo
źródło

Odpowiedzi:

661
Foo foo2();

zmień na

Foo foo2;

Wystąpił błąd, ponieważ kompilator myśli

Foo foo2()

od deklaracji funkcji o nazwie „foo2” i typie zwracanym „Foo”.

Ale w takim przypadku Jeśli zmienimy na Foo foo2, kompilator może wyświetlić błąd " call of overloaded ‘Foo()’ is ambiguous".

Mykoła Golubiew
źródło
6
Nie rozumiem, dlaczego kompilator myśli: Foo foo2 () jako deklaracja funkcji w funkcji głównej.
chaviaras michalis
Najwyraźniej już wcześniej wylądowałem na tej odpowiedzi, ponieważ nie mogę jej ponownie głosować! Oto tekstowe drugie głosowanie ... i drugie dzięki!
bigjosh,
1
Deklaracja funkcji bezparametrowej powinna mieć nakazany parametr „void”, aby to użycie było dozwolone z punktu widzenia spójności. Jeśli się nie mylę, K&R C obowiązkowo użył terminu „void”.
Rajesh
@Rajesh: Lista nieważnych parametrów nie jest obowiązkowa, oznacza tylko coś innego (parametry zerowe) od pustej listy parametrów (parametry nieokreślone).
Ben Voigt,
1
jest to kolejny dobry powód, aby przejść do jednolitej składni inicjalizacyjnej {} wprowadzonej w c ++ 11
Sumudu
41

Dla przypomnienia ...

W rzeczywistości nie jest to rozwiązanie dla twojego kodu, ale miałem ten sam komunikat o błędzie, gdy niepoprawnie uzyskiwałem dostęp do metody instancji klasy, na którą wskazuje myPointerToClassnp.

MyClass* myPointerToClass = new MyClass();
myPointerToClass.aMethodOfThatClass();

gdzie

myPointerToClass->aMethodOfThatClass();

byłoby oczywiście poprawne.

ezdazuzena
źródło
11

Dodając do bazy wiedzy, otrzymałem ten sam błąd

if(class_iter->num == *int_iter)

Mimo że IDE dało mi poprawnych członków dla class_iter. Oczywiście problem polega na tym, "anything"::iteratorże nie ma członka o nazwie, numwięc muszę go wyrejestrować. Co nie działa tak:

if(*class_iter->num == *int_iter)

...widocznie. W końcu to rozwiązałem:

if((*class_iter)->num == *int_iter)

Mam nadzieję, że to pomoże komuś, kto napotka takie pytanie, jak ja.

Matt
źródło
8

Nawias nie jest wymagany do utworzenia obiektu klasy, gdy nie zamierzasz używać sparametryzowanego konstruktora.

Wystarczy użyć Foo foo2;

To będzie działać.

Reena Cyril
źródło
7

Miałem podobny błąd, wygląda na to, że kompilator źle zrozumiał wywołanie konstruktora bez argumentów. Zrobiłem to, usuwając nawias z deklaracji zmiennej, w kodzie coś takiego:

class Foo
{
  public:
    Foo() {};
    Foo(int a) {};
    void bar() {};
};

int main()
{
  // this works...
  Foo foo1(1);
  foo1.bar();

  // this does not...
  Foo foo2; // Without "()" 
  foo2.bar();

  return 0;
}
Alexis Lopez Zubieta
źródło
1
To ta sama odpowiedź, co powyżej
Greenonline,
1
Najbardziej dokuczliwy parse nie jest tak dużo, że źle kompilator, jak to jest, że norma wymaga kompilatora do interpretowania wszystkiego, co może być deklaracja funkcji jako deklaracji funkcji, aby uniknąć niejasności.
Justin Time - Przywróć Monikę
(W szczególności pomiędzy [stmt.ambig/1]i [dcl.ambig.res/1]standard wyraźnie stwierdza, że ​​w przypadku niejasności wszystko, co można interpretować jako deklarację, JEST deklaracją, aby rozwiązać tę dwuznaczność.)
Justin Time - Przywróć Monikę
2

Natknąłem się na przypadek, w którym dostałem ten komunikat o błędzie i miałem

Foo foo(Bar());

i w zasadzie próbował przekazać tymczasowy obiekt Bar do konstruktora Foo. Okazało się, że kompilator tłumaczył to

Foo foo(Bar(*)());

to znaczy deklaracja funkcji, której nazwa to foo, która zwraca Foo pobierające argument - wskaźnik funkcji zwracający słupek z 0 argumentami. Przechodząc tymczasowo w taki sposób, lepiej użyć Bar{}zamiast Bar()eliminować dwuznaczności.

przesilenie 333
źródło
0

Jeśli chcesz zadeklarować nową substancję bez parametru (wiedząc, że obiekt ma parametry domyślne), nie pisz

 type substance1();

ale

 type substance;
Mo el
źródło
0

Z pewnością jest to przypadek narożny dla tego błędu, ale otrzymałem go w innej sytuacji, gdy próbowałem przeładować zadanie operator=. To było trochę tajemnicze IMO (od g ++ 8.1.1).

#include <cstdint>

enum DataType
{
  DT_INT32,
  DT_FLOAT
};

struct PrimitiveData
{
  union MyData
  {
    int32_t i;
    float f;
  } data;

  enum DataType dt;

  template<typename T>
  void operator=(T data)
  {
    switch(dt)
    {
      case DT_INT32:
      {
        data.i = data;
        break;
      }
      case DT_FLOAT:
      {
        data.f = data;
        break;
      }
      default:
      {
        break;
      }
    }
  }
};

int main()
{
  struct PrimitiveData pd;
  pd.dt = DT_FLOAT;
  pd = 3.4f;

  return 0;
}

Otrzymałem 2 „identyczne” błędy

error: request for member i [and 'f'] in data’, which is of non-class type float

(Równoważna błędu clangjest: error: member reference base type 'float' is not a structure or union)

dla linii data.i = data;i data.f = data;. Okazało się, że kompilator myli lokalną nazwę zmiennej „data” i moją zmienną składową data. Gdy zmieniłem to na void operator=(T newData)i data.i = newData;, data.f = newData;błąd zniknął.

Yano
źródło
0

@MykolaGolubyev już dawał wspaniałe wyjaśnienia. Szukałem rozwiązania pozwalającego zrobić coś takiegoMyClass obj ( MyAnotherClass() ) ale kompilator interpretuje to jako deklarację funkcji.

C ++ 11 ma listę stężeń inicjujących . Korzystając z tego, możemy zrobić coś takiego

Temp t{String()};

Jednak to:

Temp t(String());

zgłasza błąd kompilacji, jaki uważa tza typ Temp(String (*)()).

#include <iostream>

class String {
public:
    String(const char* str): ptr(str)
    {
        std::cout << "Constructor: " << str << std::endl;
    }
    String(void): ptr(nullptr)
    {
        std::cout << "Constructor" << std::endl;
    }
    virtual ~String(void)
    {
        std::cout << "Destructor" << std::endl;
    }

private:
    const char *ptr;
};

class Temp {
public:
    Temp(String in): str(in)
    {
        std::cout << "Temp Constructor" << std::endl;
    }

    Temp(): str(String("hello"))
    {
        std::cout << "Temp Constructor: 2" << std::endl;
    }
    virtual ~Temp(void)
    {
        std::cout << "Temp Destructor" << std::endl;
    }

    virtual String get_str()
    {
        return str;
    }

private:
    String str;
};

int main(void)
{
    Temp t{String()}; // Compiles Success!
    // Temp t(String()); // Doesn't compile. Considers "t" as of type: Temp(String (*)())
    t.get_str(); // dummy statement just to check if we are able to access the member
    return 0;
}
abhiarora
źródło