0

Why this code snippet compiles with GCC 4.9.3,

#include<memory>
#include<string>
#include<cstdio>

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;

    const auto deleter= [&ret](FILE* file){
                              if(NULL==file)
                              {
                                  ret = -1;
                              } 
                              else 
                              {
                                  ret = pclose(file);
                              }
                            };

    {
        std::unique_ptr<FILE, decltype(deleter) > pipe{popen(cmd.c_str(), "r"),  deleter};
    }

    return ret;
}

int main()
{

}

whereas this code snippet does not compile with GCC 4.9.3

#include <memory>
#include <iostream>
#include <string>
#include <cstdio>
#include<cstdio>

int ExecCmd(const std::string& cmd, std::string& )
{
    int ret = 0;
    
    auto deleter{[&ret](FILE* file){
                              if(NULL==file)
                              {
                                  ret = -1;
                              } 
                              else 
                              {
                                  ret = pclose(file);
                              }
                            }
    };
    
    {
        std::unique_ptr<FILE, decltype(deleter)> pipe(popen(cmd.c_str(), "r"), deleter);
    }
    return ret;
}

int main()
{

}

Here is what the compiler complains:

In file included from /opt/compiler-explorer/gcc-4.9.3/include/c++/4.9.3/memory:81:0,
                 from <source>:1:
/opt/compiler-explorer/gcc-4.9.3/include/c++/4.9.3/bits/unique_ptr.h: In instantiation of 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = _IO_FILE; _Dp = std::initializer_list<ExecCmd(const string&, std::string&)::<lambda(FILE*)> >]':
<source>:24:87:   required from here
/opt/compiler-explorer/gcc-4.9.3/include/c++/4.9.3/bits/unique_ptr.h:236:16: error: no match for call to '(std::unique_ptr<_IO_FILE, std::initializer_list<ExecCmd(const string&, std::string&)::<lambda(FILE*)> > >::deleter_type {aka std::initializer_list<ExecCmd(const string&, std::string&)::<lambda(FILE*)> >}) (_IO_FILE*&)'
    get_deleter()(__ptr);
                ^

Note:

  1. I only have to use GCC 4.9.3.
  2. I always use auto lambda{[](...){...}} to construct lambdas. This is the first time that I encounter such problem. And It makes dazed that should I never construct lambdas in this way in the future with GCC4.9.3? Or under what conditions, I should pay attention to such a problem?

Thanks to @J6t, @JeJo. I found how to make the code snippet complies.

I thought and thought, but I still can't find the answer.

Could anybody shed some light on this matter?

12
  • 2
    Maybe you could share the error message the compiler writes? Commented May 25, 2022 at 7:35
  • 1
    So, your question is why does auto deleter = [&..].... work, but auto deleter {[&...].... not? The const in the first version does not change anything. Commented May 25, 2022 at 7:42
  • 4
    gcc 4.9.3 has the same issue with the much smaller example auto x{1}; int y = x; - x is an initializer_list, not an int. This seems to be a bug in 4.9.3. The solution is to not combine auto with a braced initializer. (It's not clear why you would desire to do this with the lambda but not with ret.) Commented May 25, 2022 at 7:51
  • 1
    @molbdnilo Is this really a bug? It seems that 4.9.3 just does not implement N3922, which came after C++11. According to this document, it is a C++17 language future that was implemented in GCC 5. Commented May 25, 2022 at 7:55
  • 1
    @DanielLangr It seems to me that you are 100% correct, and it was a defect in the language, not in gcc. Sufficiently old versions of icc and clang do the same thing. Commented May 25, 2022 at 7:57

1 Answer 1

2

The problem with the second version is that, in C++11 and C++14, the following initialization:

auto var{ whatever }; 

yields the type of var to be of std::initializer_list<something>.

This changed in C++17 which accepted the N3922 paper. This paper was adopted by GCC since version 5. Your 4.9.3 thus deduces std::initializer_list, which is not what you want.


I always use auto lambda{[](...){...}} to construct lambdas.

Bottom line: Don't do this with GCC 4.9.3.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.