为什么此 C++ 代码中的构造函数不明确,我该如何解决?
Why is the constructor in this C++ code ambiguous and how do I fix it?
在下面的代码中,编译器无法确定我要使用哪个构造函数。为什么,我该如何解决这个问题? (Live example)
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename eval_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
inline explicit constexpr A(const std::function<data_type(a_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "idx_type" << std::endl;
}
inline explicit constexpr A(const std::function<data_type(b_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
int a = 1;
long long b = 2;
auto c = A<double, double, long long, int>{
[](std::tuple<long long,int> p)->double { return 1.0*std::get<0>(p) / std::get<1>(p); },
[](double d)->double { return d; }, b,a
};
return 0;
}
它不起作用的原因是因为 lambda 不是 std::function
,因此编译器尝试使用构造函数的 the fifth 重载来创建一个。问题是,由于这种转换,您的两个 A
构造函数都可以使用,并且 std::tuple<long long,int>
和 std::tuple<std::size_t,std::size_t>
可以相互构造的原因使得编译器选择什么构造函数变得模棱两可.
你可以做的是显式转换为所需的 std::function
(评论中使用了 @PasserBy 的 MCVE),like this:
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
A(const std::function<data_type(a_type)>&)
{
std::cout << "idx_type" << std::endl;
}
A(const std::function<data_type(b_type)>&)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
std::function<double(std::tuple<long long, int>)> func = [](auto p) -> double { return 1; };
auto c = A<double, long long, int>{
func
};
}
正如@SombreroChicken 提到的,std::function<R(Args...)>
有一个构造函数允许 any 可调用对象 c
初始化它,只要 c(Args...)
有效并且 returns 可以转换为 R
.
要修复它,您可以使用一些 SFINAE 机器
#include <tuple>
#include <functional>
#include <iostream>
#include <type_traits>
template<typename data_type, typename Type1, typename Type2>
class A
{
template<typename T>
struct tag
{
operator T();
};
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<b_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "size_t" << std::endl;
}
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<a_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "other" << std::endl;
}
};
int main()
{
auto c = A<double, long long, int>{
[](std::tuple<long long, int> p) -> double { return 1; }
};
auto c2 = A<double, long long, int>{
[](std::tuple<std::size_t, std::size_t>) -> double { return 2; }
};
}
在这里,如果可以分别使用 b_type
或 a_type
调用可调用对象,我们将关闭构造函数。通过 tag
的额外间接是为了禁用不同类型的元组之间的转换
在下面的代码中,编译器无法确定我要使用哪个构造函数。为什么,我该如何解决这个问题? (Live example)
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename eval_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
inline explicit constexpr A(const std::function<data_type(a_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "idx_type" << std::endl;
}
inline explicit constexpr A(const std::function<data_type(b_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
int a = 1;
long long b = 2;
auto c = A<double, double, long long, int>{
[](std::tuple<long long,int> p)->double { return 1.0*std::get<0>(p) / std::get<1>(p); },
[](double d)->double { return d; }, b,a
};
return 0;
}
它不起作用的原因是因为 lambda 不是 std::function
,因此编译器尝试使用构造函数的 the fifth 重载来创建一个。问题是,由于这种转换,您的两个 A
构造函数都可以使用,并且 std::tuple<long long,int>
和 std::tuple<std::size_t,std::size_t>
可以相互构造的原因使得编译器选择什么构造函数变得模棱两可.
你可以做的是显式转换为所需的 std::function
(评论中使用了 @PasserBy 的 MCVE),like this:
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
A(const std::function<data_type(a_type)>&)
{
std::cout << "idx_type" << std::endl;
}
A(const std::function<data_type(b_type)>&)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
std::function<double(std::tuple<long long, int>)> func = [](auto p) -> double { return 1; };
auto c = A<double, long long, int>{
func
};
}
正如@SombreroChicken 提到的,std::function<R(Args...)>
有一个构造函数允许 any 可调用对象 c
初始化它,只要 c(Args...)
有效并且 returns 可以转换为 R
.
要修复它,您可以使用一些 SFINAE 机器
#include <tuple>
#include <functional>
#include <iostream>
#include <type_traits>
template<typename data_type, typename Type1, typename Type2>
class A
{
template<typename T>
struct tag
{
operator T();
};
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<b_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "size_t" << std::endl;
}
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<a_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "other" << std::endl;
}
};
int main()
{
auto c = A<double, long long, int>{
[](std::tuple<long long, int> p) -> double { return 1; }
};
auto c2 = A<double, long long, int>{
[](std::tuple<std::size_t, std::size_t>) -> double { return 2; }
};
}
在这里,如果可以分别使用 b_type
或 a_type
调用可调用对象,我们将关闭构造函数。通过 tag
的额外间接是为了禁用不同类型的元组之间的转换