7

I noticed that libstdc++'s implementation of std::ignore takes a const T& argument, which can't bind to a volatile rvalue. Hence the following code fails to compile:

#include <tuple>
#include <utility>
struct C {};
using VC = C volatile;
int main() {
    std::tuple<VC> t;
    std::tie(std::ignore) = std::move(t);
}

(http://coliru.stacked-crooked.com/a/7bfc499c1748e59e)

Is this in violation of the standard, or is there a clause that renders this undefined behaviour?

Brian Bi
  • 111,498
  • 10
  • 176
  • 312

2 Answers2

1

I'm not a language lawyer, so I'm going to be answering this question as directly as possible.

ignore is found in the synopsis of tuple in tuple.general as such:

// [tuple.creation], tuple creation functions:
const unspecified ignore;

As you noticed, the libstdc++ implementation defines ignore like this:

  // A class (and instance) which can be used in 'tie' when an element
  // of a tuple is not required
  struct _Swallow_assign
  {
    template<class _Tp>
      const _Swallow_assign&
      operator=(const _Tp&) const
      { return *this; }
  };

Whereas the libc++ version defines it like this:

template <class _Up>
struct __ignore_t
{
    template <class _Tp>
        _LIBCPP_INLINE_VISIBILITY
        const __ignore_t& operator=(_Tp&&) const {return *this;}
};

As such, it compiles in libc++. Now the definition of std::tie can be found in [tuple.creation] which says:

Returns: tuple<Types&...>(t...). When an argument in t is ignore, assigning any value to the corresponding tuple element has no effect.

This doesn't say anything about ignore itself, so I'm going to chalk this up to unspecified behavior. You can argue it's undefined behavior by omission, but that might be stretching it.

  • 1
    I'm not sure I understand. Doesn't that specify the behaviour well enough so that the code I posted should have *no effect*, which does not appear to give the implementation license to reject the code? – Brian Bi Jun 20 '16 at 22:41
  • @Brian *No effect* is in itself under-specified. `std::tie` has no *effects* clause. The `operator=` in `ignore` does nothing but `return *this`, so is a no-op, which perfectly fits under one definition of*no effects*. Either way, the code is rejected because of how the library is implemented, not because of a violation of any of the clauses under `std::tie`. – uh oh somebody needs a pupper Jun 20 '16 at 22:45
  • No, it doesn't say the function `std::tie` has no effects; it says that "assigning any value to the corresponding tuple element has no effect". – Brian Bi Jun 20 '16 at 22:46
  • @Brian That's not what I said. I said it has no *effects* clause, which is sometimes present and sometimes not. The only wording present is *no effects*. So the only thing that follows is pure speculation. (I edited my comment above incase you thought I meant `tie` was a no-op). – uh oh somebody needs a pupper Jun 20 '16 at 22:49
1

Comment:

// g++ 4.8.4
int main() {
    volatile int vi;
    std::ignore = vi;

    // error: no match for ‘operator=’ (
    //     operand types are ‘const std::_Swallow_assign’
    //     and ‘std::remove_reference<volatile int&>::type {aka volatile int}’
    // )
    // std::ignore = std::move(vi);

    // However this compiles:
    volatile int&& vir = std::move(vi);
    std::ignore = vir;
}