将指针传递给在 C++ 中动态定义的内置类型
passing a pointer to a built-in type defined on the fly in c++
我想调用一个库的函数(我不能修改)
void function(int* i, char* c);
有没有办法即时调用定义 int
和 char
的函数?
即做类似
的事情
function(&1, &'v');
而不是
int int_1 = 1;
char char_1 = 'v';
function(&int_1, &char_v);
这将极大地减少我的代码的长度,同时增加可读性。
Why would you want to do so? 将变量作为非常量指针传递表明被调用者打算修改参数那是通过的,因此它不能是右值。
所以传递一个指向文字的指针是没有意义的(除非它是一个不同的字符串文字)。此外,对您来说很明显,您无法确定文字的地址,因为它不可寻址。唯一有意义的常量是空指针或指向可寻址内存的某个绝对地址
正如其他人指出的那样,答案是否定的...
你可以通过重载来模拟它 function
:
void function(int i, char c)
{
function(&i, &c);
}
所以现在你可以写function(1, 'v')
是的 - 你可以。
function((int*)&(const int &)1, (char*)&(const char &)'v');
只要指针在函数调用后不被取消引用,这是完全合法的。这是因为它们的临时生命周期等于函数调用所在的完整表达式。
它们可以被函数用来修改数据而不会出现任何可能的问题。
生活example。请注意,函数 'function' 未定义。该示例仅说明这样的函数调用是完全有效的。
注意:此语法的复杂性是由于某些 'C++' 安全措施造成的。毕竟传递指向未命名数据的指针是很少见的事情。然而,这并不意味着这个结构是非法的或UB。
FISOCPP 的回答很好,但我不喜欢创建临时文件的方式。
可以这样用复合横向语法来完成:
function(&(int){1}, &(char){'v'});
当您尝试获取地址时,这两种使用临时方法都会导致 gcc 发出警告,尽管它是完美定义的有效代码。
有趣的是,由于复合横向在 C 中有自动存储而不是在 C++ 中临时存储,所以如果在 C99 模式下编译甚至不会有警告。
你可以在 C++11 中做到这一点:
#include <type_traits>
#include <iostream>
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::true_type, std::false_type)
{
return arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::false_type, std::true_type)
{
return &arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary (Arg&& arg)
{
return take_address_if_necessary_impl <Param> (
arg,
typename std::is_convertible <Arg, Param>::type {},
typename std::is_convertible <typename std::add_pointer <Arg>::type, Param>::type {}
);
}
template <typename Ret, typename... Params, typename... Args>
Ret call_special (Ret (*f) (Params...), Args&&... args)
{
return f (take_address_if_necessary <Params, Args> (args)...);
}
template <typename... Params, typename... Args>
void call_special (void (*f) (Params...), Args&&... args)
{
f (take_address_if_necessary <Params> (args)...);
}
void function (int* i, char* c)
{
std::cout << *i << ' ' << *c << std::endl;
}
int main ()
{
int i = 42;
char c = '%';
call_special (function, 1, 'f');
call_special (function, &i, '?');
call_special (function, &i, &c);
}
以上程序产生
1 f
42 ?
42 %
如您所料。
这里有一些注意事项:首先,如果您尝试使用重载函数,这将失败,因为 C++ 无法将重载函数推导为函数指针:
void bar (int);
void bar (float);
call_special (bar, 3.0f); // Compiler error
您或许可以使用显式模板参数解决此问题:
call_special <float> (bar, 3.0f); // Works
或者当然是显式输入函数:
call_special ((void (*) (float))bar, 3.0f);
其次,为了简单起见,call_special
和它的帮助者在价值 类 上玩得很随意。它可能需要更多的工作才能稳健地处理右值、const 值等。
第三,既可以作为值也可以作为指针传递的参数将不起作用。事实上,一般的转换可能会让人头疼:
void quux (long* i)
{
if (i)
std::cout << *i << std::endl;
else
std::cout << "(null)" << std::endl;
}
call_special (quux, NULL); // What does this print?
你最好使用函数来显式获取地址:
template <typename T>
T* foo (T&& t)
{
return &t;
}
function (foo (3), foo ('7'));
请注意,无论您使用什么方法,您都将处理临时对象,它们在分号处消失。因此,如果您使用的库存储了对您提供的参数的引用,您别无选择,只能显式地为参数提供存储空间。
可以赋值或获取其地址的东西是左值。
无法获取其地址,也无法分配给 (sortof) 的东西是右值。
这是一个接受右值并将其转换为左值的函数。只要源右值有效,这个左值就有效,希望它足够长:
template<class T>
T& as_lvalue(T&& t){return t;}
template<class T>
void as_lvalue(T&)=delete;
然后我们按如下方式使用它:
function(&as_lvalue(1), &as_lvalue('v'));
as_lvalue
大致是std::move
的相反操作,否则可以称为as_rvalue
。不完全相反,因为我让左值到左值的转换产生了错误。
现在,一元 &
可以被重载。所以我们可以这样写:
template<class T>
T* addressof_temporary(T&& t) {
return std::addressof( as_lvalue(std::forward<T>(t)) );
}
然后调用:
function(addressof_temporary(1), addressof_temporary('v'));
如果偏执。
我想调用一个库的函数(我不能修改)
void function(int* i, char* c);
有没有办法即时调用定义 int
和 char
的函数?
即做类似
的事情function(&1, &'v');
而不是
int int_1 = 1;
char char_1 = 'v';
function(&int_1, &char_v);
这将极大地减少我的代码的长度,同时增加可读性。
Why would you want to do so? 将变量作为非常量指针传递表明被调用者打算修改参数那是通过的,因此它不能是右值。
所以传递一个指向文字的指针是没有意义的(除非它是一个不同的字符串文字)。此外,对您来说很明显,您无法确定文字的地址,因为它不可寻址。唯一有意义的常量是空指针或指向可寻址内存的某个绝对地址
正如其他人指出的那样,答案是否定的...
你可以通过重载来模拟它 function
:
void function(int i, char c)
{
function(&i, &c);
}
所以现在你可以写function(1, 'v')
是的 - 你可以。
function((int*)&(const int &)1, (char*)&(const char &)'v');
只要指针在函数调用后不被取消引用,这是完全合法的。这是因为它们的临时生命周期等于函数调用所在的完整表达式。
它们可以被函数用来修改数据而不会出现任何可能的问题。
生活example。请注意,函数 'function' 未定义。该示例仅说明这样的函数调用是完全有效的。
注意:此语法的复杂性是由于某些 'C++' 安全措施造成的。毕竟传递指向未命名数据的指针是很少见的事情。然而,这并不意味着这个结构是非法的或UB。
FISOCPP 的回答很好,但我不喜欢创建临时文件的方式。
可以这样用复合横向语法来完成:
function(&(int){1}, &(char){'v'});
当您尝试获取地址时,这两种使用临时方法都会导致 gcc 发出警告,尽管它是完美定义的有效代码。
有趣的是,由于复合横向在 C 中有自动存储而不是在 C++ 中临时存储,所以如果在 C99 模式下编译甚至不会有警告。
你可以在 C++11 中做到这一点:
#include <type_traits>
#include <iostream>
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::true_type, std::false_type)
{
return arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::false_type, std::true_type)
{
return &arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary (Arg&& arg)
{
return take_address_if_necessary_impl <Param> (
arg,
typename std::is_convertible <Arg, Param>::type {},
typename std::is_convertible <typename std::add_pointer <Arg>::type, Param>::type {}
);
}
template <typename Ret, typename... Params, typename... Args>
Ret call_special (Ret (*f) (Params...), Args&&... args)
{
return f (take_address_if_necessary <Params, Args> (args)...);
}
template <typename... Params, typename... Args>
void call_special (void (*f) (Params...), Args&&... args)
{
f (take_address_if_necessary <Params> (args)...);
}
void function (int* i, char* c)
{
std::cout << *i << ' ' << *c << std::endl;
}
int main ()
{
int i = 42;
char c = '%';
call_special (function, 1, 'f');
call_special (function, &i, '?');
call_special (function, &i, &c);
}
以上程序产生
1 f
42 ?
42 %
如您所料。
这里有一些注意事项:首先,如果您尝试使用重载函数,这将失败,因为 C++ 无法将重载函数推导为函数指针:
void bar (int);
void bar (float);
call_special (bar, 3.0f); // Compiler error
您或许可以使用显式模板参数解决此问题:
call_special <float> (bar, 3.0f); // Works
或者当然是显式输入函数:
call_special ((void (*) (float))bar, 3.0f);
其次,为了简单起见,call_special
和它的帮助者在价值 类 上玩得很随意。它可能需要更多的工作才能稳健地处理右值、const 值等。
第三,既可以作为值也可以作为指针传递的参数将不起作用。事实上,一般的转换可能会让人头疼:
void quux (long* i)
{
if (i)
std::cout << *i << std::endl;
else
std::cout << "(null)" << std::endl;
}
call_special (quux, NULL); // What does this print?
你最好使用函数来显式获取地址:
template <typename T>
T* foo (T&& t)
{
return &t;
}
function (foo (3), foo ('7'));
请注意,无论您使用什么方法,您都将处理临时对象,它们在分号处消失。因此,如果您使用的库存储了对您提供的参数的引用,您别无选择,只能显式地为参数提供存储空间。
可以赋值或获取其地址的东西是左值。
无法获取其地址,也无法分配给 (sortof) 的东西是右值。
这是一个接受右值并将其转换为左值的函数。只要源右值有效,这个左值就有效,希望它足够长:
template<class T>
T& as_lvalue(T&& t){return t;}
template<class T>
void as_lvalue(T&)=delete;
然后我们按如下方式使用它:
function(&as_lvalue(1), &as_lvalue('v'));
as_lvalue
大致是std::move
的相反操作,否则可以称为as_rvalue
。不完全相反,因为我让左值到左值的转换产生了错误。
现在,一元 &
可以被重载。所以我们可以这样写:
template<class T>
T* addressof_temporary(T&& t) {
return std::addressof( as_lvalue(std::forward<T>(t)) );
}
然后调用:
function(addressof_temporary(1), addressof_temporary('v'));
如果偏执。