【问题标题】:passing a pointer to a built-in type defined on the fly in c++传递指向在 C++ 中动态定义的内置类型的指针
【发布时间】:2015-06-28 22:56:22
【问题描述】:

我想调用一个库的函数(我不能修改)

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

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

即做类似的事情

function(&1, &'v');

而不是

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

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

【问题讨论】:

  • 您确定增加可读性部分吗?
  • function(&1, &'v') 如何提高可读性?
  • 尝试将指针更改为“指向 const 的指针”?
  • @user3528438 仍然行不通-您不能指向文字,这在概念上没有意义。 (更准确地说,您不能将指针指向右值,而只能指向左值。)
  • 使用非指针参数重载函数,将它们存储在临时变量中,然后使用指针调用原始函数。

标签: c++ pass-by-pointer


【解决方案1】:

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

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

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

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

【讨论】:

  • 如果我打电话给function(1, someCharToBeMofified),这会不会很危险? someCharToBeMofified应该会被修改,但事实并非如此。
  • 如果函数修改了指向的数据,那么整个问题就变得没有意义了。正确使用 const 会使这些意图变得清晰。
  • 虽然@FISOCPP 的回答表明这是可能的,但这个包装器是一个很好的方法——如果它被解释性的 cmets 覆盖。
【解决方案2】:

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

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

【讨论】:

  • 我见过 C API,无论好坏,都需要 OP 的行为。并不是说我会那样设计我的代码。
  • 该函数提供了一些我不会使用的高级功能。因此,一些参数在编译时是已知的(甚至更早:))
  • @PeterM:同意,但这超出了这个问题的范围。
  • 我不同意它超出范围,至于 char 部分,function( .., "v") 是有效的,这与 OP 想要做的非常接近。这样做只会浪费一个字符。
【解决方案3】:

是的 - 你可以。

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

只要在函数调用后指针没有被取消引用,它是完全合法的。这是因为它们的临时生命周期等于函数调用所在的完整表达式。

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

生活example。请注意,未定义功能“功能”。这个例子只是说明这样的函数调用是完全有效的。

注意:这种语法的复杂性是由于某些“C++”安全措施造成的。毕竟传递一个指向未命名数据的指针是你很少做的事情。但这并不意味着这个结构是非法的或 UB。

【讨论】:

  • 两种状态要求:减少代码&提高可读性。不确定你是否真的见过:-)
  • 关于合法性:不是被调用的函数修改了指针。
  • 不——这是完全合法的。我敢肯定。对这种结构有很长时间的经验。另请阅读 ISO 标准。但是我现在无法给出确切的报价。
  • 我会让您找到说明剥离该 const 并随后修改该值是合法的(您当然不能省略 const),但由于生成了一个临时我想它可以“工作”。
【解决方案4】:

FISOCPP 的回答很好,但我不喜欢临时创建的方式。

用复合横向语法可以做到这一点:

function(&(int){1}, &(char){'v'});

使用临时地址的两种方式都会导致 gcc 在您尝试获取地址时发出警告,尽管它是完美定义的有效代码。

有趣的是,由于复合横向在 C 中具有自动存储而不是在 C++ 中的临时存储,因此如果在 C99 模式下编译甚至不会出现警告。

【讨论】:

    【解决方案5】:

    您可以在 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'));
    

    请注意,无论您使用哪种方法,您都将处理临时对象,它们以分号结尾。因此,如果您使用的库存储了对您给它的参数的引用,那么您别无选择,只能显式地为参数提供存储空间。

    【讨论】:

      【解决方案6】:

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

      不能获取地址,也不能分配给 (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_lvaluestd::move 的操作大致相反,否则可以称为as_rvalue。并非完全相反,因为我让左值到左值的转换产生了错误。

      现在,一元 &amp; 可以重载。所以我们可以这样写:

      template<class T>
      T* addressof_temporary(T&& t) {
        return std::addressof( as_lvalue(std::forward<T>(t)) );
      }
      

      然后调用:

      function(addressof_temporary(1), addressof_temporary('v'));
      

      如果偏执。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-05
        • 1970-01-01
        • 2012-09-14
        • 2015-05-19
        • 1970-01-01
        • 2021-10-16
        • 2020-08-16
        相关资源
        最近更新 更多