2016-03-22 2 views
5

У меня есть шаблон матрицы класса:Как разрешить этот неоднозначный вызов конструктора шаблонов?

#include <iostream> 
#include <array> 
#include <initializer_list> 
#include <utility> 
#include <type_traits> 
#include <cstddef> 

enum ColumnFill { 
    COLUMNS 
}; 

template <typename T, std::size_t M, std::size_t N> 
struct TMatrixMxN { 
    TMatrixMxN(T x = T(0)) { 
     std::cout << "Default" << std::endl; 
    } 

    TMatrixMxN(std::initializer_list<T> values) { 
     std::cout << "Row initializer" << std::endl; 
    } 

    TMatrixMxN(std::initializer_list<T> values, ColumnFill dummy) { 
     std::cout << "Column initializer" << std::endl; 
    } 

    TMatrixMxN(std::initializer_list<std::initializer_list<T>> values) { 
     std::cout << "Value initializer" << std::endl; 
    } 

    TMatrixMxN(const std::array<std::array<T, N>, M> &values) { 
     std::cout << "From array" << std::endl; 
    } 

    TMatrixMxN(const TMatrixMxN<T, M - 1, N - 1> &x) { 
     std::cout << "From lower dimension" << std::endl; 
    } 

    TMatrixMxN(const TMatrixMxN &x) { 
     std::cout << "Copy" << std::endl; 
    } 

    TMatrixMxN(TMatrixMxN &&x) { 
     std::cout << "Move" << std::endl; 
    } 
}; 

typedef TMatrixMxN<float, 1, 1> Matrix1x1; 
typedef TMatrixMxN<float, 2, 2> Matrix2x2; 
typedef TMatrixMxN<float, 3, 3> Matrix3x3; 
typedef TMatrixMxN<float, 3, 1> Matrix3x1; 

денди Everything, пока я не использовать матрицу, которая имеет 1 в любой из его размеров:

int main() { 
    std::array<std::array<float, 3>, 3> arr{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}; 
    Matrix3x3 m1; 
    Matrix3x3 m2({1, 2, 3}); 
    Matrix3x3 m3({1, 2, 3}, COLUMNS); 
    Matrix3x3 m4({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); 
    Matrix3x3 m5(arr); 
    Matrix3x3 m6(Matrix2x2({{1, 2}, {3, 4}})); 
    Matrix3x3 m7(m6); 
    Matrix3x3 m8(std::move(m7)); 

    std::cout << std::endl; 

    TMatrixMxN<float, 3, 2>({{1, 2}, {3, 4}, {5, 6}}); 

    std::cout << std::endl; 

    // PROBLEMS: 
    Matrix3x1({{1}, {2}, {3}}); // error: ambiguous 
    Matrix1x1({{1}});   // error: ambiguous 
} 

Я не знаю, как красиво решить эту проблему (Я хочу, чтобы эти вызовы назвать стоимостью инициализатора конструктора)

при компиляции с g++ -std=c++11 Ambiguous.cpp, компилятор считает, что следующие конструкторами являются кандидатами для 3х1 матрицы: двигаться, копию, из-низшего измерения, стоимость инициализатор, рядного инициализатор. Для матрицы 1x1 также перечислены из массива и по умолчанию.

Что я пробовал:

  • Использование SFINAE, чтобы состояние перегрузки, что T должен быть арифметический тип (std::is_arithmetic), потому что я думал, что это иногда может подумать, что это initializer_list, но ничего не изменил. Я понял, что это бесполезно проверка, так как он на самом деле уже знает хорошо, и хорошо, что Т поплавок в этом примере
  • Добавление explicit ключевого слова в эти конструкторы: ценностных инициализатору, рядного инициализатор, колонного инициализатора , из-массива и от нижележащего измерении, потому что я думал, что есть некоторые неявные вызовы происходит, но и ничего не
  • Making ЬурейуЮ «фил» изменить (как «список инициализатора поплавка ») и преобразование кода в {fil{1}}, так как я понял, что готовый материал не может t интерпретироваться как список инициализаторов - тогда он работал по назначению. Однако я не думаю, что это исправление достаточно хорошо.

Можно ли это вообще сделать?

ответ

3

Неоднозначность в одном-мерном случае находится между этими двумя конструкторами:

TMatrixMxN(std::initializer_list<T> values); 
TMatrixMxN(std::initializer_list<std::initializer_list<T>> values) 

Поскольку {{1}, {2}, {3}} также может быть истолковано как излишне агрессивно рамно версии {1, 2, 3}. Обратите внимание, что это не размер N или M, что имеет значение, это по-прежнему не удается:

Matrix3x3 m9({{1}, {2}, {3}}); // same error 

Самого простое решением является отключением неоднозначности поворота менее желанным конструктора в конструктор шаблона с фиктивными параметрами:

template <size_t _=M> 
TMatrixMxN(std::initializer_list<T> values) { 
    std::cout << "Row initializer" << std::endl; 
} 

Мы ничего не изменили - мы только что создали этот шаблон. Теперь, если оба конструктора совпадают, предпочтительнее использовать initializer_list<initializer_list<T>>, поскольку это не шаблон.Если вы действительно хотите отключить этот для M==1 или N==1, можно добавить enable_if_t в там:

template <size_t m=M, size_t n=N, class = std::enable_if_t<(m != 1 && n != 1)>> 
TMatrixMxN(std::initializer_list<T> values) { 
    std::cout << "Row initializer" << std::endl; 
} 
+0

Спасибо! :) Не могли бы вы показать мне, как я мог сделать это с помощью enable_if_t? –

+1

@ComradeBearabyte Смотрите править. – Barry

+0

Я вижу, я сделал то же самое, за исключением того, что я не добавлял новые параметры шаблона (m = M, n = N) и просто использовал M и N, но это не помогло. Не могли бы вы объяснить, почему мне нужно новое параметры шаблона? –

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