В своих проектах я использую boost-variant
исчерпывающе. Следовательно, для моих модульных тестов мне нужно проверить содержимое варианта на определенный T
с определенным содержанием t
,
Так что я разработал функцию cmpVariant
для этой единственной цели и для удаления беспорядка из моих модульных тестов.
В некоторых случаях тип T
не оснащен operator==
чтобы пользователь мог передать функцию, удовлетворяющую EqualityCompare Требование (https://en.cppreference.com/w/cpp/named_req/EqualityComparable)
Теперь по какой-то непонятной причине следующий код не компилируется. Это говорит о том, что нет соответствующей функции?
Clang 6.0.1 Ошибка компилятора
prog.cc:22:5: error: no matching function for call to 'cmpVariant'
cmpVariant(number, 3.2, lambdaEquiv); // Fails!
^~~~~~~~~~
prog.cc:6:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-1 &, const type-parameter-0-1 &)>' against '(lambda at prog.cc:19:24)'
bool cmpVariant(
^
1 error generated.
Кто-нибудь знает почему?
Код
#include <iostream>
#include <boost/variant.hpp>
#include <functional>
template<typename V, typename T>
bool cmpVariant(
const V& variant,
const T& t,
const std::function<bool(const T& u, const T& v)>& equiv = [](const T& u, const T& v) {return u == v; })
{
if (variant.type() != typeid(t)) return false;
auto v = boost::get<T>(variant);
return equiv(v, t);
}
int main(int, char**) {
boost::variant<double, int> number{ 3.2 };
cmpVariant(number, 3.2);
auto lambdaEquiv = [](const double& x, const double& y) { return x == y; };
std::function<bool(const double&, const double&)> equiv = lambdaEquiv;
cmpVariant(number, 3.2, equiv); // Works!
cmpVariant(number, 3.2, lambdaEquiv); // Fails!
}
Компилятор не может сопоставить лямбду с типом параметра функции. Вы можете исправить это, явно создав экземпляр функции:
cmpVariant<boost::variant<double, int>, double>(number, 3.2, equiv);
Это явно немного многословно, так что есть еще одна возможность изменить объявление вашей функции на
template<typename V, typename T, typename Fct = std::function<bool(const T& u, const T& v)>>
bool cmpVariant(
const V& variant,
const T& t,
Fct&& f = [](const T& u, const T& v) {return u == v; })
{ /* Same as before. */ }
который можно назвать так
cmpVariant(number, 3.2, equiv); // Type deduction works now.
Улучшение, предложенное @DanielLangr в комментариях, заключается в использовании std::equal_to
.
template<typename V, typename T, typename Fct = std::equal_to<T>>
bool cmpVariant(
const V& variant,
const T& t,
Fct&& f = std::equal_to<T>{})
{ /* Again, same as before. */ }
Одним из преимуществ здесь является избавление от std::function
и его часто ненужные накладные расходы.
Принятие аргумента компаратора делает вывод проблематичным, поэтому вы можете захотеть изменить компаратор в параметр шаблона (возможно, избегая создания тяжелого std::function
объект):
template<typename T> class t_EquilityComparator
{
public: bool operator ()(const T& u, const T& v) const { return u == v; }
};
template<typename V, typename T, typename Comparator = t_EquilityComparator<T>>
bool cmpVariant(
const V& variant,
const T& t,
const Comparator & equiv = Comparator{})
{
if (variant.type() != typeid(t)) return false;
auto v = boost::get<T>(variant);
return equiv(v, t);
}
int main(int, char**) {
boost::variant<double, int> number{ 3.2 };
cmpVariant(number, 3.2);
auto equiv = [](const double& x, const double& y) { return x == y; };
cmpVariant(number, 3.2, equiv); // This line fails to compile! Why?
}