6

I upgraded to the latest release of Google Test, and several of my tests no longer compiled. I've reduced it to this:

#include <gtest/gtest.h>

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>

int main () {
    const std::string foo = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const auto uppers = std::count_if(foo.begin(), foo.end(), std::isupper);
    std::cout << "There are " << uppers << " capitals." << std::endl;
    return 0;
}

The Visual Studio 2019 (16.10.4) compiler with /std:c++latest complains:

1>Source.cpp(10,30): error C2672: 'std::count_if': no matching overloaded function found
1>Source.cpp(10,75): error C2780: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty2>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>::difference_type std::count_if(_ExPo &&,const _FwdIt,const _FwdIt,_Pr) noexcept': expects 4 arguments - 3 provided
1>algorithm(570): message : see declaration of 'std::count_if'
1>Source.cpp(10,30): error C2783: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>::difference_type std::count_if(_InIt,_InIt,_Pr)': could not deduce template argument for '_Pr'
1>algorithm(553): message : see declaration of 'std::count_if'

If I comment out the inclusion of gtest.h, the code builds and executes correctly.

What could gtest.h be doing that messes up template argument deduction for a call that depends only on std-defined types and functions?

[Note, my question is not how to workaround the problem, but to understand the specific underlying cause. I have a workaround: Replace the std::isupper with a lambda.]

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 3
    My guess, one of your headers is now including ``, which introduces `template< class charT > bool isupper( charT ch, const locale& loc )` which means `std::isupper` is "now" overloaded and can't resolved. Does it work if you use `const auto uppers = std::count_if(foo.begin(), foo.end(), []()(auto ch){ return std::isupper(static_cast(ch));} )` instead? – NathanOliver Jul 23 '21 at 14:28
  • 1
    Verify that by replacing `` with ``. I think that's the answer, as you can see in CPPreference that `std::isupper` is overloaded in _different_ header files. – JDługosz Jul 23 '21 at 14:39
  • @NathanOliver: Brilliant deduction! I was thinking of `std::isupper` as a generic classifier--a sort `foo` function that returns `bool`. I completely forgot about the nonsense that `` brings in, and I hadn't expected Google Test to sneak it in. If you post an answer, the points are yours. Thanks also to @JDługosz for the verification tip! – Adrian McCarthy Jul 23 '21 at 17:21

1 Answers1

3

It appears that <gtest/gtest.h> is now including <locale>, which introduces

template< class charT > bool isupper( charT ch, const locale& loc ) 

into the scope. That means that std::isupper now has two possible functions it could point to and without you specifying which one to use, you get an ambiguity which causes template argument deduction to fail.

If you do go the lambda route to fix this, make sure you cast the input to std::isupper to an unsigned char like

const auto uppers = std::count_if(foo.begin(), 
                                  foo.end(), 
                                  []()(auto ch){ return std::isupper(static_cast<unsigned char>(ch));} )
NathanOliver
  • 171,901
  • 28
  • 288
  • 402