如何将指针传递给没有临时变量的整数?

时间:2019-05-07 02:49:57

标签: c++ temporary

在我过去的多种语言中,有一种方法可以传递整数常量/字面量作为参考,以避免不必要地为函数创建极短寿命的变量。一个很好的例子是setsockopt调用的重用变量。例如

int reuseVal = 1;    
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuseVal, sizeof (reuseVal));

某些语言允许您执行以下操作

setsockopt(s, SOL_SOCKET, SO_REUSEADDR, %ref(1), sizeof (int));

C ++中有类似的方法吗?

3 个答案:

答案 0 :(得分:2)

C ++没有内置功能可以满足您的要求,但是您可以自己构建一个(为清晰起见,需要额外记录):

template <typename T>
class holster
{
public:
    using value_type = T;

    template <typename... U>
    holster(U&&... args)
        : _val(std::forward<U>(args)...)
    {
        std::cout << "CTOR: holster" << std::endl;
    }

    ~holster()
    {
        std::cout << "DTOR: holster" << std::endl;
    }

    value_type* ptr() { return &_val; }

private:
    value_type _val;
};

这种类型的用法非常简单:

struct thing
{
    thing()
    {
        std::cout << "CTOR: thing" << std::endl;
    }

    ~thing()
    {
        std::cout << "DTOR: thing" << std::endl;
    }
};

void foo(thing*)
{
    std::cout << "in foo" << std::endl;
}

int main()
{
    foo(holster<thing>().ptr());
    return 0;
}

将其扩展回原始示例:

setsockopt(s, SOL_SOCKET, SO_REUSEADDR, holster<int>(1).ptr(), sizeof (int));

为什么这样做? C ++生存期的鲜为人知的功能是,为传递给函数而创建的任何临时项都会在函数持续时间内进行生命周期扩展。 保证参照对象(holster::_val继续存在,直到foo返回,并且在评估下一个完整表达式之前(在这种情况下,在{{ 1}})。

  

§6.6.7/4 [class.temporary]

     

当实现引入具有非平凡构造函数([class.default.ctor][class.copy.ctor])的类的临时对象时,应确保为该临时对象调用构造函数。   同样,析构函数应被调用为具有非平凡析构函数([class.dtor])的临时对象。   临时对象被销毁,这是评估(按词法包含)创建点的完整表达式([intro.execution])的最后一步。   即使该评估以引发异常结束也是如此。   破坏临时对象的值计算和副作用仅与完整表达式相关,而与任何特定子表达式无关。

答案 1 :(得分:1)

您不能直接这样做。在C语言中,您可以使用复合文字(例如(int []){ 1 }),但这在C ++中不可用。

但是,您可以编写如下的辅助函数:

template<typename T>
struct temp_holder {
    T data;
    explicit temp_holder(T x) : data(x) {}
    T *ptr() { return &data; }
    const T *ptr() const { return &data; }
};

template<typename T>
temp_holder<T> make_temporary(T x) {
    temp_holder<T> tmp(x);
    return tmp;
}

现在您可以这样做:

setsockopt(s, SOL_SOCKET, SO_REUSEADDR, make_temporary(1).ptr(), sizeof (int));

在我使用g ++进行的测试中,这会在高于0的任何优化级别(即int reuseVal = 1&reuseVal或{{1}上生成与-O / -O2版本相同的代码}}。

如果要避免定义新的类型和函数,可以稍微滥用标准库:

-O3

这种方法的缺点是它要经过动态内存分配(加上(异常安全)释放),因此它可能比自定义类型(或仅声明一个临时变量)要慢。

答案 2 :(得分:0)

setsockopt()需要一个指向已分配变量的指针。不管该变量是静态分配还是动态分配,都没有关系,只要它具有可访问的内存地址即可在运行时获取该地址,然后取消引用该地址以访问该地址的数据即可。

您的原始代码确实做到了 。它声明一个在自动内存中(即在调用线程的调用堆栈中)分配的局部变量,然后使用&操作符的地址获取该变量的内存地址,并将该地址传递给{ {1}}。事情没有比这更简单的了。

您得到的其他答案只是同一主题的更复杂的变化-为变量分配值,然后获取该变量的地址。

要节省声明原始类型的变量并填充数据的最小开销,可以在编译时使用全局变量静态地执行该操作,该全局变量在程序启动时初始化一次,并在需要时重用,例如:

setsockopt()

我想像像其他语言中的const int cReuseAddrOn = 1; void setReuseAddrOn(int s) { setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &cReuseAddrOn, sizeof (cReuseAddrOn)); } 之类的东西可能会像这样实现,创建对临时分配的变量或可重用文字常量的引用。