将指针传递给在 C++ 中动态定义的内置类型

passing a pointer to a built-in type defined on the fly in c++

本文关键字:定义 动态 内置 置类型 C++ 指针      更新时间:2023-10-16

我想调用库的函数(我无法修改)

void function(int* i, char* c);

有没有办法即时调用定义intchar的函数?

即做类似的事情

function(&1, &'v');

而不是

int int_1 = 1;
char char_1 = 'v';
function(&int_1, &char_v);

这将大大减少我的代码长度,同时提高可读性。

正如其他人所指出的,答案是否定的......

您可以通过重载function来模拟它:

void function(int i, char c)
{
function(&i, &c);
}

所以现在你可以写function(1, 'v')

你为什么要这样做? 将变量作为非常量指针传递表示被调用方打算修改传递的参数,因此它不能是右值。

因此,将指针传递给文本将毫无意义(除非它是不同的字符串文本)。此外,很明显,您无法确定文字的地址,因为它是不可寻址的。唯一有意义的常量是空指针或指向可寻址内存的某个绝对地址

是的 - 你可以。

function((int*)&(const int &)1, (char*)&(const char &)'v');

只要在函数调用后不取消引用指针,这是完全合法的。这是因为它们具有临时的生存时间,等于函数调用存在的完整表达式。

函数可以使用它们来修改数据,而不会出现任何可能的问题。

生活的例子。请注意,函数"函数"未定义。该示例仅演示此类函数调用是完全有效的。

注意:此语法的复杂性是由于一些"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及其助手在值类上玩得又快又松。可能需要做更多的工作才能可靠地处理右值、常量值等。

第三,可以作为值和指针传递的参数将不起作用。事实上,一般来说,转换可能会引起头痛:

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'));

请注意,无论您使用哪种方法,您都将处理临时变量,它们在分号处死亡。因此,如果您正在使用的库存储了对您提供给它的参数的引用,则您别无选择,只能显式地为参数提供存储。

可以分配给或获取其地址的东西是一个左值。

不能被占用其地址或被分配(排序)的东西是一个右值。

这是一个采用右值并将其转换为左值的函数。 仅当源 rvalue 有效时,此左值才有效,希望足够长:

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'));

如果偏执。