2012-04-06 4 views
8

В следующем (рабочем) примере кода функция templated register_enum() используется для перебора перечисления и вызова предоставленного пользователем обратного вызова для преобразования значения перечисления в c-строку. Все перечисления определены внутри класса, а преобразование перечисления в строку выполняется со статической функцией to_cstring (enum). Если класс (например, класс шейдера ниже) имеет более одного перечисления и соответствующей перегруженной функции to_cstring (enum), компилятор не может решить, какая функция является правильной to_cstring(), чтобы перейти к register_enum(). Я думаю, что код объясняет лучше, чем я могу ...Как устранить эту ошибку <неразрешенный перегруженный тип> при использовании std :: function?

#include <functional> 
#include <iostream> 

// Actual code uses Lua, but for simplification 
// I'll hide it in this example. 
typedef void lua_State; 

class widget 
{ 
    public: 
     enum class TYPE 
     { 
      BEGIN = 0, 
      WINDOW = BEGIN, 
      BUTTON, 
      SCROLL, 
      PROGRESS, 
      END 
     }; 

     static const char *to_cstring(const TYPE value) 
     { 
      switch (value) 
      { 
       case TYPE::WINDOW: return "window"; 
       case TYPE::BUTTON: return "button"; 
       case TYPE::SCROLL: return "scroll"; 
       case TYPE::PROGRESS: return "progress"; 
       default: break; 
      } 
      return nullptr; 
     } 
}; 

class shader 
{ 
    public: 
     enum class FUNC 
     { 
      BEGIN = 0, 
      TRANSLATE = BEGIN, 
      ROTATE, 
      SCALE, 
      COLOR, 
      COORD, 
      END 
     }; 

     enum class WAVEFORM 
     { 
      BEGIN = 0, 
      SINE = BEGIN, 
      SQUARE, 
      TRIANGLE, 
      LINEAR, 
      NOISE, 
      END 
     }; 

     static const char *to_cstring(const FUNC value) 
     { 
      switch (value) 
      { 
       case FUNC::TRANSLATE: return "translate"; 
       case FUNC::ROTATE: return "rotate"; 
       case FUNC::SCALE: return "scale"; 
       case FUNC::COLOR: return "color"; 
       case FUNC::COORD: return "coord"; 
       default: break; 
      } 
      return nullptr; 
     } 

     static const char *to_cstring(const WAVEFORM value) 
     { 
      switch (value) 
      { 
       case WAVEFORM::SINE: return "sine"; 
       case WAVEFORM::SQUARE: return "square"; 
       case WAVEFORM::TRIANGLE: return "triangle"; 
       case WAVEFORM::LINEAR: return "linear"; 
       case WAVEFORM::NOISE: return "noise"; 
       default: break; 
      } 
      return nullptr; 
     } 
}; 

// Increment an enum value. 
// My compiler (g++ 4.6) doesn't support type_traits for enumerations, so 
// here I just static_cast the enum value to int... Something to be fixed 
// later... 
template < class E > 
E &enum_increment(E &value) 
{ 
    return value = (value == E::END) ? E::BEGIN : E(static_cast<int>(value) + 1); 
} 

widget::TYPE &operator++(widget::TYPE &e) 
{ 
    return enum_increment<widget::TYPE>(e); 
} 

shader::FUNC &operator++(shader::FUNC &e) 
{ 
    return enum_increment<shader::FUNC>(e); 
} 

shader::WAVEFORM &operator++(shader::WAVEFORM &e) 
{ 
    return enum_increment<shader::WAVEFORM>(e); 
} 


// Register the enumeration with Lua 
template< class E > 
void register_enum(lua_State *L, const char *table_name, std::function< const char*(E) > to_cstring) 
{ 
    (void)L; // Not used in this example. 
    // Actual code creates a table in Lua and sets table[ to_cstring(i) ] = i 
    for (auto i = E::BEGIN; i < E::END; ++i) 
    { 
     // For now, assume to_cstring can't return nullptr... 
     const char *key = to_cstring(i); 
     const int value = static_cast<int>(i); 
     std::cout << table_name << "." << key << " = " << value << std::endl; 
    } 
} 

int main(int argc, char **argv) 
{ 
    (void)argc; (void)argv; 

    lua_State *L = nullptr; 

    // Only one to_cstring function in widget class so this works... 
    register_enum<widget::TYPE>(L, "widgets", widget::to_cstring); 

    // ... but these don't know which to_cstring to use. 
    register_enum<shader::FUNC>(L, "functions", shader::to_cstring); 
    //register_enum<shader::WAVEFORM>(L, "waveforms", shader::to_cstring); 

    return 0; 
} 

Compiler выход:

$ g++ -std=c++0x -Wall -Wextra -pedantic test.cpp -o test && ./test 
test.cpp: In function ‘int main(int, char**)’: 
test.cpp:140:69: error: no matching function for call to ‘register_enum(lua_State*&, const char [10], <unresolved overloaded function type>)’ 
test.cpp:140:69: note: candidate is: 
test.cpp:117:7: note: template<class E> void register_enum(lua_State*, const char*, std::function<const char*(E)>) 

Как передать правильную функцию to_cstring в register_enum()? Я понимаю, что могу переименовать отдельные функции to_cstring(), но я хотел бы избежать этого, если это возможно. Возможно, мой дизайн вонючий, и вы можете рекомендовать лучший подход.

Мой вопрос похож на Calling overloaded function using templates (unresolved overloaded function type compiler error) и How to get the address of an overloaded member function?, но до сих пор я не могу применить эту информацию к своей конкретной проблеме.

ответ

6

Ошибка говорит вам, что есть две потенциальные перегрузки, которые могут быть использованы, и компилятор не может решить для вас. С другой стороны, вы можете определить, какой из них использовать, используя бросок:

typedef const char *(*func_ptr)(shader::FUNC); 
register_enum<shader::FUNC>(L, "functions", (func_ptr)shader::to_cstring); 

Или без ЬурейеГо (в труднее читать однострочник):

register_enum<shader::FUNC>(L, "functions", 
      (const char *(*)(shader::FUNC))shader::to_cstring); 

* Примечания что в сигнатурах функций удаляется верхний уровень const.

Следующий вопрос: почему компилятор не нашел соответствующую перегрузку сам по себе? Проблема заключается в том, что в вызове register_enum вы передаете тип перечисления и определяет тип std::function как std::function< const char* (shader::FUNC) >, но std::function имеет шаблонный конструктор и перед тем как попытаться вывести тип аргумента конструктору , компилятор должен знать, какую перегрузку вы хотите использовать.

-1

Я бы сказал, что в этом случае, если компилятор не может решить между двумя перегрузками, вы должны переименовать один из них! Это предотвратит дальнейшее двусмысленное использование функций.

+0

Почему downvote? это гораздо более простое решение. – xcski

Смежные вопросы