gpt4 book ai didi

c++ - 使用 std::is_assignable、boost::function 和 nullptr 时出现意外结果

转载 作者:IT老高 更新时间:2023-10-28 23:19:56 25 4
gpt4 key购买 nike

以下表达式使用 is_assignable返回 true使用 gcc 4.7 和 boost 1.49 时:

typedef boost::function<void()> F;
std::is_assignable<F, std::nullptr_t>::value

但是,此代码无法编译:
boost::function<void()> f;
f = nullptr;

产生这些错误信息:
In file included from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/detail/maybe_include.hpp:13:0,
from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/detail/function_iterate.hpp:14,
from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47,
from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function.hpp:64,
from ..\main.cpp:8:
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp: In instantiation of 'static void boost::detail::function::void_function_obj_invoker0<FunctionObj, R>::invoke(boost::detail::function::function_buffer&) [with FunctionObj = std::nullptr_t; R = void]':
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:907:60: required from 'void boost::function0<R>::assign_to(Functor) [with Functor = std::nullptr_t; R = void]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:722:7: required from 'boost::function0<R>::function0(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:1042:16: required from 'boost::function<R()>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:1083:5: required from 'typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R()>&>::type boost::function<R()>::operator=(Functor) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R()>&>::type = boost::function<void()>&]'
..\main.cpp:172:6: required from here
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:153:11: error: '* f' cannot be used as a function

此外,此表达式返回 false :
typedef boost::function<void()> G;
std::is_assignable<G, decltype(NULL)>::value

但这段代码确实可以编译:
boost::function<void()> g;
g = NULL;
is_assignable的结果似乎没有正确反射(reflect) boost::function 的功能.我在这里做错了吗? (我无法理解错误消息。)

我认为类型特征应该是确定模板中使用的类的功能的可靠方法。 C++11 中提供的类型特征是否与 boost::function 不兼容?

为了提供一些上下文,我一直在从事几个个人项目,以更好地熟悉 C++11 的新特性。对于这个特定的项目,我试图创建一个类来存储可以“停用”的可调用函数。这大致是我想要做的:
template <typename F>
class callable_function
{
public:
callable_function(F func) : func_(func)
{
/* func_ is initially active */
}

void call()
{
if (/* func_ is active */) func_();
}

void deactivate()
{
/* set func_ to deactive */
}

private:
F func_;
};

对于 /* func_ is active *//* set func_ to deactive */块,我想提供两种不同的实现,它们在编译时根据 F 的属性选择。 .如 nullptr可以分配给 func_func_可以在 bool 上下文中使用,然后我想使用以下内容(这是为内置函数指针和 std::function 选择的内容):
template <typename F>
class callable_function
{
public:
callable_function(F func) : func_(func) {}

void call()
{
if (func_) func_();
}

void deactivate()
{
func_ = nullptr;
}

private:
F func_;
};

nullptr不能分配给 func_ ,然后我想在存储“事件”状态的类中存储一个额外的 bool 值。为仿函数和 lambda 函数选择此实现:
template <typename F>
class callable_function
{
public:
callable_function(F func) : func_(func), active_(true) {}

void call()
{
if (active_) func_();
}

void deactivate()
{
active_ = false;
}

private:
F func_;
bool active_;
};

nullptr当前无法分配给 boost::function ,我希望选择第二个实现。但是,由于 is_assignable正在返回 trueboost::functionnullptr ,改为选择第一个实现,这会导致 deactivate 中的编译错误功能。

最佳答案

[我对回答我自己的问题感到难过,但由于我已经了解了很多关于它的知识,我认为最好在这里合并这些信息。杰西在帮助我理解这一切方面起了很大作用,所以请在上面点赞他的评论。]

那么,为什么is_assignable返回以下结果:

typedef boost::function<void()> F;
std::is_assignable<F, std::nullptr_t>::value // true
std::is_assignable<F, decltype(NULL)>::value // false

尽管这些陈述似乎与这些结果相矛盾:
boost::function<void()> f;
f = nullptr; // fails to compile
f = NULL; // compiles correctly

首先要注意的是,标准库的任何基于操作的类型特征( is_constructibleis_assignableis_convertible 等)只检查具有匹配给定类型的有效接口(interface)的函数模板。特别是,当这些类型被替换到函数体中时,它们不会检查该函数的实现是否有效。
boost::function没有 nullptr 的特定构造函数,但它确实有一个“包罗万象”的模板赋值运算符(以及相应的构造函数):
template<typename Functor>
BOOST_FUNCTION_FUNCTION& operator=(Functor const & f);

这是 nullptr 的最佳匹配,因为 std::nullptr_t 没有特定的过载并且这个不需要任何转换为​​另一种类型(除了转换为 const & )。因为模板替换找到了这个赋值运算符, std::is_assignable<boost::function<void()>, std::nullptr_t>返回 true .

然而,在这个函数的主体中, Functor预计是可调用类型;也就是说, f();预计是一个有效的陈述。 nullptr不是可调用对象,因此,以下代码会导致问题中列出的编译器错误:
boost::function<void()> f;
f = nullptr; // fails to compile

但是为什么 std::is_assignable<boost::function<void()>, decltype(NULL)>返回 false ? boost::function int 没有特定的赋值运算符参数,那么为什么不使用与 int 相同的“全能”模板赋值运算符和 std::nullptr_t ?

早些时候,我通过省略元编程方面来简化此赋值运算符的代码,但由于它们现在相关,我将重新添加它们:
template<typename Functor>
typename enable_if_c<
(boost::type_traits::ice_not<
(is_integral<Functor>::value)>::value),
BOOST_FUNCTION_FUNCTION&>::type
operator=(Functor const & f)

元编程结构 enable_if_c 应该是不言而喻的。这里用来防止当参数类型为 int时实例化这个赋值运算符。 (即,当 is_integral 返回 true 时)。因此,当赋值语句的右侧是 int 类型时, boost::function 没有匹配的赋值运算符.这就是为什么 std::is_assignable<boost::function<void()>, decltype(NULL)>返回 false , 自 NULL类型为 int (至少对于 GCC)。

但这仍然不能解释为什么 f = NULL;正确编译。为了解释这一点,重要的是要注意值 0可以隐式转换为任何指针类型。 boost::function通过使用接受指向私有(private)结构的指针的赋值运算符来利用这一点。 (以下是来自 boost::function 的代码的大大简化版本,但足以证明我的观点):
namespace boost
{
template<typename R()>
function
{
private:
struct clear_type {}
//...

public:
BOOST_FUNCTION_FUNCTION& operator=(clear_type*);
//...
};
}

clear_type是私有(private)结构,任何外部代码都无法创建它的实例。此赋值运算符唯一可以接受的值是从 0 隐式转换的空指针。 .这是使用表达式 f = NULL; 调用的赋值运算符.

这解释了为什么 is_assignable并且赋值语句按照它们的方式工作,但它仍然不能帮助我解决我原来的问题:如何检测给定类型是否可以接受 nullptrNULL ?

不幸的是,我仍然受到类型特征的限制,因为它们只能检测是否存在有效的接口(interface)。对于 nullptr ,似乎没有好的答案。与 boost::function , nullptr 确实存在有效的接口(interface),但是函数体的实现对于这个类型是无效的,对于 f = nullptr;这样的语句总是会导致编译错误.

但是我能正确检测到 NULL可以分配给给定的类型,例如 boost::function ,在编译时? std::is_assignable要求我提供第二个参数的类型。我们已经知道 decltype(NULL)不会工作,因为这评估为 int .我可以用 boost::function<void()>::function::clear_type*作为类型,但这非常冗长,并且需要我知道我正在使用的类型的内部细节。

一个优雅的解决方案是创建一个自定义类型特征,它来自 another post here on SO 中的 Luc Danton。 .我不会描述这种方法的细节,因为它们在另一个问题中有更好的解释,但是我的自定义类型特征的代码可以在这里看到:
template<typename> struct Void { typedef void type; };

template<typename T, typename Sfinae = void>
struct is_assignable_with_NULL: std::false_type {};

template<typename T>
struct is_assignable_with_NULL<T,
typename Void< decltype( std::declval<T>() = NULL ) >::type
>: std::true_type {};

我可以像 std::is_assignable 一样使用这个新的类型特征,但我只需要在左侧提供对象的类型:
is_assignable_by_NULL<boost::function<void()>::value

像所有类型特征一样,这仍然只会检查有效的接口(interface),而忽略函数体的有效性,但它最终让我能够正确确定是否可以将 NULL 分配给 boost::function。 (和任何其他类型)在编译时。

关于c++ - 使用 std::is_assignable、boost::function 和 nullptr 时出现意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10220316/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com